home *** CD-ROM | disk | FTP | other *** search
/ Dr. Windows 3 / dr win3.zip / dr win3 / MISCELLA / WASP1_8.ZIP / WASP1-8.WRI < prev   
Encoding:
Text File  |  1993-08-08  |  99.5 KB  |  2,316 lines

  1. Ever since I agreed to discuss some aspects (no pun intended <G>) of the
  2. Windows Aspect programming language here, I have been trying to decide
  3. on an approach to use. I have now written three opening "lectures", and
  4. tossed each of them. I just can't seem to find a way to get started
  5. properly.
  6.  
  7. There are, of course, some rather major, and many minor, differences in
  8. how commands operate in the two languages (DOS Aspect and Windows
  9. Aspect), but pinning these differences down in a "definitive" list is
  10. not what I plan on doing with this series.
  11.  
  12. Part of the problem is that the differences between DOS Aspect, covered
  13. so well already by David and others in the DOS Aspect "course", and
  14. Windows Aspect, are many, but at times can be quite subtle. A lot of the
  15. basic difference is a way of thinking, which results from the fact that
  16. Windows is a multi-tasking environment.
  17.  
  18. I suspect that the easiest method of doing this course is to simply
  19. start writing a basic log on script (since I am most familiar with
  20. PCBoard systems, we'll do it for that type <G>). Since some differences
  21. in the two languages are based on commands that don't even exist under
  22. DOS Aspect, we will concentrate the first portion of this "course", on a
  23. script that uses commands only available in the Windows version of
  24. Aspect to allow us to check for almost any prompt under certain
  25. circumstances. Then, once we have a basic log in script done, we'll look
  26. at multi-tasking under Wasp, using Windows dialog boxes for user input,
  27. and taking advantage of the unique file format INI to easily store and
  28. access information.
  29.  
  30. In the process, we'll also look at the PCP/Win Dialog Editor, what a
  31. User Window is, and how to use the PCP/Win User Window Builder to design
  32. one, and some other uniquely Windows features of Wasp.
  33.  
  34. So, in this "lesson" we'll look at :
  35.  
  36.     - the WHEN TARGET/WAITFOR conflict in Wasp
  37.     - Using TERMGETS to eliminate WAITFOR commands
  38.     - Using STRFIND with TERMGETS
  39.  
  40.  
  41. The WHEN TARGET / WAITFOR conflict under Wasp
  42. ---------------------------------------------
  43.  
  44. Wasp has a couple of advantages over DOS Aspect, one basic one being far
  45. more possible WHEN commands. However, like DOS Aspect, the most commonly
  46. used WHEN during a log on (WHEN TARGET INDEX) is limited to just three
  47. possible targets. As you are probably well aware, during a log on to
  48. most systems (we'll use PCBoard as an example, since I am most familiar
  49. with that, and finally build a version of my PCBMail script for handling
  50. QWK packets on PCBoard systems), there are far more than just three
  51. possible prompts which can appear. As a result, DOS Aspect programmers
  52. either have to combine the WHEN TARGET command with WAITFOR commands, or
  53. continually change the WHEN TARGET commands to account for variations in
  54. which prompt to look for.
  55.  
  56. Neither is an ideal solution, especially when you enter the realm of
  57. Wasp. Here, even more so than in DOS Aspect, combining a WHEN TARGET and
  58. a WAITFOR can be more troublesome than it is worth. The problem lies in
  59. the differing nature of the two commands.
  60.  
  61. Basically, a WHEN allows the script to continue running while it remains
  62. active. If the target of the WHEN appears, it interrupts the rest of the
  63. script to perform whatever action is expected of it when that target
  64. appears. This, of course, makes WHEN TARGET ideal for use to catch
  65. prompts which only MAY appear, or may appear in various places, rather
  66. than just once, in sequence, during a log in.
  67.  
  68. A WAITFOR, on the other hand, does not allow the script to continue
  69. running while it is active. Everything must halt until either the target
  70. of the WAITFOR is seen, or the WAITFOR has timed out (since it is a
  71. timed command, with a default wait of 30 seconds). This makes this
  72. command fine if you are looking for a prompt which normally ALWAYS
  73. appears, and generally, does so in the same spot during the log in.
  74.  
  75. The problem exists, though, in how the WAITFOR operates. As above, the
  76. rest of the script must stop running while it is active.
  77.  
  78. THIS INCLUDES THE ACTIONS OF ANY WHEN TARGET THAT IS ACTIVE.
  79. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  80.  
  81. In other words, while a WAITFOR is the active command, a WHEN TARGET
  82. cannot carry out it's functions, even if it's target is "seen", until
  83. after the WAITFOR has either been satisfied, or has timed out.
  84.  
  85. To see how this can affect us, let's look at a sample "scenario". This
  86. is not a real system, but rather made up prompts to illustrate the
  87. point, so please don't complain that you've never seen these prompts on
  88. a PCBoard system <GG>.
  89.  
  90. You are logging on to this imaginary board. If you have new mail on the
  91. system, it will tell you so, and then ask if you want to read it now
  92. (prompt "Read it now [Y/n]? "). However, if you there is no mail for
  93. you, nothing appears. After the new mail scan, a prompt ("Scan for new
  94. files [Y/n]?") always appears. Logically, a script segment to handle
  95. this might be :
  96.  
  97.    when target 0 "Read it now [Y/n]?" call send_no
  98.    waitfor "Scan for new files [Y/n]?" 30
  99.    if success
  100.       transmit "Y^M"
  101.    endif
  102.  
  103. So... what's the problem here?? Well, since the script continues running
  104. after the WHEN TARGET is set up, the WAITFOR becomes the active command.
  105. If you do have new mail, the "Read it now" prompt will appear, and the
  106. WHEN TARGET will notice it, BUT, because of the nature of the two
  107. commands, it can not take action based on the target while the WAITFOR
  108. is active. Instead, it must wait for the WAITFOR to go inactive, in this
  109. case, since the "Scan for new files" prompt won't appear until after the
  110. script answers the new mail prompt, for 30 seconds. It will then answer
  111. the new mail prompt, but the new files prompt will be missed (since the
  112. WAITFOR timed out.)
  113.  
  114. Obviously, this is no good. There are a couple of "work around"
  115. solutions to this. The one which I recommend most often, if you really
  116. want to continue using the WAITFOR, is as follows :
  117.  
  118.    when target 0 "Read it now [Y/n]?" call send_no
  119.    tries = 1
  120.    while tries < 31
  121.       waitfor "Scan for new files [Y/n]?" 1
  122.       if success
  123.          transmit "Y^M"
  124.          exitwhile
  125.       endif
  126.       tries++
  127.    endwhile
  128.  
  129. How does this work differently than the earlier version? Well, the
  130. WAITFOR is only active for 1 second at a time. Once it is inactive, the
  131. script takes some nanoseconds of time to work through the
  132. WHILE...ENDWHILE construct. During these nanoseconds, the WHEN TARGET is
  133. able to interrupt the WHILE...ENDWHILE, and send the response to the new
  134. mail prompt. Since this brings up the new files prompt, the WAITFOR will
  135. see it and respond also.
  136.  
  137. But this routine can be rather difficult to maintain throughout a
  138. script, since it MUST be maintained for any WAITFOR used while a WHEN
  139. TARGET is active, in order to have things operate properly. So, is there
  140. an easier way??
  141.  
  142.  
  143. Using the TERMGETS command to find prompts
  144. ------------------------------------------
  145.  
  146. PCBoard systems actually make things easy for a Wasp script programmer.
  147. One thing I noticed as I attempted to write a script for a "generic"
  148. PCBoard system is that virtually ALL PCBoard prompts contain a question
  149. mark ("?"), usually at the end of the prompt. (Some other types of BBS
  150. software use a colon (":") to mark the end of a prompt, still others an
  151. exclamation point ("!")... if you look, you might find a "common" key to
  152. watch for the system you use most often)
  153.  
  154. So, it should be possible then, to use a single WHEN TARGET, and watch
  155. for that "?", then respond to the prompt sent. But how can you determine
  156. which prompt is sent, and thus, respond correctly to it?
  157.  
  158. Fortunately, Wasp provides the solution in the form of the TERMGETS
  159. command, a command not available in DOS Aspect. This little fellow is
  160. really quite nice. What it does is simple, actually... it reads the line
  161. on screen (i.e. on the terminal) at a specified row and from a specified
  162. column position to another specified column position. To accompany it,
  163. Wasp also provides two system variables that can be used, $ROW, which is
  164. the current row on the terminal, and $COL, which is the current cursor
  165. column position on the terminal.
  166.  
  167. So, if we set up the following command :
  168.  
  169.    termgets $ROW 0 prompt_str $COL
  170.  
  171. we will have, as the contents of the variable, prompt_str, the text of
  172. the current row on the terminal, from the beginning (index 0), to the
  173. current cursor position. Since PCBoard systems always leave the cursor
  174. on the terminal at the end of the prompt, and waits there for a
  175. response, this TERMGETS command will return the variable, prompt_str,
  176. which will be the current prompt that PCBoard is waiting for a response
  177. to.
  178.  
  179. Neat, huh? (DOS Aspect programmers... suffer! <GG>)
  180.  
  181. Using these two "tricks", we can build the beginning of a Wasp PCBoard
  182. log on script, which will not use any WAITFOR commands at all....
  183.  
  184. proc main
  185.    when target 0 "?" call get_prompt
  186. endproc
  187.  
  188. proc get_prompt
  189.    string prompt_str
  190.    termgets $ROW 0 prompt_str $COL
  191. endproc
  192.  
  193. Of course, we still don't do anything with the prompt_str once we have
  194. it, but first, we have to do one other thing to make the script work for
  195. us.
  196.  
  197. Earlier, I said that WHEN TARGET allows the script to continue running
  198. when it is active, which makes it a multi-tasking command. However, in
  199. the above segment, this is BAD, as the script will continue running
  200. right out of the main procedure, and thus, the script. So, believe it or
  201. not, we have to actually STOP the script from continuing here, until we
  202. are ready for it to go on. This code will handle that :
  203.  
  204. integer holding
  205.  
  206. proc main
  207.    when target 0 "?" call get_prompt
  208.    holding = 1
  209.    while holding
  210.    endwhile
  211. endproc
  212.  
  213. proc get_prompt
  214.    string prompt_str
  215.    termgets $ROW 0 prompt_str $COL
  216. endproc
  217.  
  218. This stops the script from continuing, as long as the value of holding
  219. is 1, but since it is through a WHILE...ENDWHILE construct, the WHEN
  220. TARGET will continue to operate as we want.
  221.  
  222. The reason that holding is declared as a global variable is that we are
  223. going to want to be able to change the value of it in later script
  224. segments, in order to allow the script to continue running when we wish
  225. it to.
  226.  
  227. The next step is to respond to the prompts....
  228.  
  229.  
  230. Applying the STRFIND command to prompt_str
  231. ------------------------------------------
  232.  
  233. OK, so this is relatively easy, and I'm sure a lot of you have already
  234. figured out what to use in order to find out which prompt is on the
  235. terminal. For those of you who haven't, the command we want is STRFIND.
  236.  
  237. Simply put, this command searches a string for a second "target" string.
  238. It can be tested for the system value "found" for the search. This means
  239. that we can use STRFIND to look for certain key words in the prompt_str
  240. variable, and based on the "found" system value for that action, take
  241. certain other actions.
  242.  
  243. Before we go on, let's discuss the concept of key words.
  244.  
  245. Many beginning script programmers tend to fall into a wicked trap, which
  246. can bite back unexpectedly. The trap they fall into is watching for the
  247. entire prompt from a system, before responding to it. This has many
  248. drawbacks. Perhaps the two biggest are...
  249.  
  250. 1) If you are using a board with ANSI color, it is quite possible that
  251. there will be ANSI codes in any given prompt string. Since WHEN TARGET
  252. and WAITFOR get "raw" data from the terminal, the presence of ANSI codes
  253. in a prompt string can result in the script not seeing it as the same as
  254. the one you are looking for. Since many sysops change these colors
  255. frequently, the ANSI codes can change, resulting in a failure of your
  256. script to recognize the prompt.
  257.  
  258. 2) Many sysops will "customize" the prompts on their system. When they
  259. do so, they will add things like your first name, etc. using the
  260. @variables of PCBoard systems, but this will cause problems when you are
  261. looking for a prompt. A string to look for on one system, may not work
  262. on the next.
  263.  
  264. As a result, most Wasp scripters that I know try to watch for a minimal
  265. amount of text, being only what is absolutely necessary to distinguish
  266. one prompt from another. Again, PCBoard systems make this fairly easy
  267. for a scripter, through using "standardized" wording of the prompts sent
  268. by them.
  269.  
  270. So, what we want to do is to use STRFIND to locate one of those
  271. "minimal" key words in the prompt_str variable, and thus, result in a
  272. response from the script. We'll start with several of the most common
  273. PCBoard key words....
  274.  
  275. integer holding
  276.  
  277. proc main
  278.    when target 0 "?" call get_prompt
  279.    holding = 1
  280.    while holding
  281.    endwhile
  282. endproc
  283.  
  284. proc get_prompt
  285.    string prompt_str
  286.    termgets $ROW 0 prompt_str $COL
  287.    if strfind prompt_str "first name"
  288.       transmit $USERID
  289.       transmit "^M"
  290.    elseif strfind prompt_str "Password (Dots"
  291.       transmit $PASSWORD
  292.       transmit "^M"
  293.    elseif strfind prompt_str "Command"
  294.       holding = 0
  295.    endif
  296. endproc
  297.  
  298. This is pretty basic, and does not really need the use of a WHEN TARGET,
  299. but it leads up to the next posting, so we'll let it ride. Basically,
  300. this script will look for the name prompt, and send the system variable,
  301. $USERID (which is the user name in the dialing directory entry for this
  302. system). It also looks for the password prompt, and sends the system
  303. variable, $PASSWORD (as above, but for the password entry in the dialing
  304. directory).
  305.  
  306. The last one is perhaps the one which needs explanation... When the
  307. script sees the key word "Command", it sets the value of holding to 0,
  308. but does nothing else. Why? Well, this ends the WHILE...ENDWHILE loop in
  309. proc main, and exits the script. Since the first place that the key word
  310. "Command" will appear on a PCBoard system is at the "Main Board
  311. Command?" prompt, we are finished with our log in, and no longer need
  312. the script running. So, we set holding to 0, and the script drops
  313. through the WHILE...ENDWHILE, and stops running.
  314.  
  315. ------------------------------------------------------------------------
  316.  
  317. Last time, we wrote a VERY basic log on script for a PCBoard system.
  318. Today, we're going to work on making it more flexible, and even more
  319. "generic" (i.e. usable with just about any PCBoard system).
  320.  
  321. So, our "topics" for today are :
  322.  
  323.    - adding additional prompt key words
  324.    - using multiple key words for a single prompt
  325.    - creating and using custom FUNCTION procedures to eliminate
  326.      some IF/ELSE/ENDIF construct problems
  327.  
  328.  
  329. Adding additional PCBoard prompts
  330. ---------------------------------
  331.  
  332. Obviously, the above script does not account for more than the very
  333. basic prompts of a PCBoard system. So now, we'll add a few more of the
  334. more common ones.
  335.  
  336. Most PCBoard systems will ask you either if you want ANSI graphics, or
  337. Color graphics. The standard prompt includes the word "graphics", but
  338. generally, if you are asked if you want Color, the word "graphics"
  339. doesn't appear in the prompt. For simplicity, we'll say no to graphics
  340. on these systems (which also avoids concerns about ANSI codes in our key
  341. word phrases <G>).
  342.  
  343. So, to cover both possible prompts, we need look for either the key
  344. word "graphics" or the key word "Color", and then respond with a "N".
  345. BTW, a lot of people are not aware of it, but you can log on to a
  346. PCBoard system fairly quickly by responding "N Q NS" to the graphics
  347. prompt. This tells PCBoard that you don't want graphics ("N"), you want
  348. a quick log on ("Q"), and you want to skip viewing any NEWS files at log
  349. on ("NS"). So, that's the response we'll send.
  350.  
  351. This is the first "real" test of our use of WHEN TARGET, since
  352. generally, one or the other prompt will appear on most PCBoard systems,
  353. but not both. So our WHEN TARGET finally comes into use by watching for
  354. a "possible" prompt, rather than a "probable" prompt.
  355.  
  356. One other common log on prompt on PCBoard systems is the language
  357. prompt. This asks which available language you want to use. Again, for
  358. simplicity, we'll accept the default system language. If you study the
  359. prompt that generally appears for this, you'll find it normally is
  360.  
  361. "Language to use (Enter)=no change?"
  362.  
  363. or a variation thereof, but invariably, ending in "(Enter)=no change?"
  364.  
  365. But we'll also add several other "common" PCBoard key words. These
  366. include the "More?" prompt, those which end in "(Enter)=yes?", those
  367. which end in "(Enter)=no?", and lastly, those which ask if you want to
  368. "continue?".
  369.  
  370. ; Windows Aspect PCBoard log on script
  371.  
  372. integer watchfor    ; I've changed this for consistency with my PCBLOG
  373.                     ; script, which we'll end up with
  374.  
  375. proc main
  376.    when target 0 "?" call get_prompt
  377.    watchfor = 1
  378.    while watchfor
  379.    endwhile
  380. endproc
  381.  
  382. proc get_prompt
  383.    string prompt_str
  384.    termgets $ROW 0 prompt_str $COL
  385.    if strfind prompt_str "first name"
  386.       transmit $USERID
  387.       transmit "^M"
  388.    elseif strfind prompt_str "Password (Dots"
  389.       transmit $PASSWORD
  390.       transmit "^M"
  391.    elseif strfind prompt_str "=no change"
  392.       transmit "^M"
  393.    elseif strfind prompt_str "graphics"
  394.       transmit "N Q NS^M"
  395.    elseif strfind prompt_str "Color"
  396.       transmit "N Q NS^M"
  397.    elseif strfind prompt_str "Command"
  398.       watchfor = 0
  399.    elseif strfind prompt_str "More?"
  400.       transmit "N^M"
  401.    elseif strfind prompt_str "Enter)=yes?"
  402.       transmit "N^M"
  403.    elseif strfind prompt_str "Enter)=no?"
  404.       transmit "^M"
  405.    elseif strfind prompt_str "continue?"
  406.       transmit "^M"
  407.    endif
  408. endproc
  409.  
  410. If you look at the code above, you will notice a certain "inelegance"
  411. about it, that being that we send the same response to two possible
  412. STRFIND commands, in several cases. One way to simplify this, is to call
  413. a procedure to send the response. A simpler way, would be to look for
  414. either "graphics" OR "Color" in the prompt_str variable.
  415.  
  416. Since STRFIND is a "found" type of command, I have learned that
  417. only one of these (or one "success/failure" type) can be tested per IF
  418. statement. If you try to test two STRFIND commands by using the "||"
  419. (or) operator, Wasp will give compile errors, about missing tokens (try
  420. it yourself).
  421.  
  422. This brings us to another "trick" I use.....
  423.  
  424.  
  425. Creating and Using a FUNCTION call instead of a Wasp command
  426. ------------------------------------------------------------
  427.  
  428. To avoid the compile errors, and yet, still keep the script as simple
  429. (and "elegant") as possible, we need to test for multiple keywords in a
  430. prompt_str, key words that all require the same response. But, as noted
  431. above, we can't do this using STRFIND in an IF statement
  432.  
  433. In both DOS Aspect, and Wasp, a FUNCTION is a procedure which performs
  434. operations on various items in a script, and based on the results of
  435. those operations, "returns" a value (be it numeric or string) back to
  436. the point where the FUNCTION was called. In effect, the FUNCTION call in
  437. the original location is replaced by the value that is returned by the
  438. FUNCTION called.
  439.  
  440. This is ideal for our problem above, as we would no longer be testing
  441. for "found", but rather, testing a series of numeric values (if our
  442. FUNCTION returned a numeric, or integer, value). So basically what we
  443. want to do, is to pass the key word to the FUNCTION, have it check for
  444. that key word in the prompt_str, and return an integer value based on
  445. the result of the STRFIND command.
  446.  
  447. To do this, we need two things in our script.... a change of the
  448. declaration of prompt_str from a local variable to a global (so the
  449. FUNCTION has access to it), and a function to return the value as an
  450. integer. The function is actually quite simple....
  451.  
  452. func CheckPrompt:integer
  453.    strparm chk_out
  454.    strfind prompt_str chk_out
  455.    return found
  456. endfunc
  457.  
  458. which takes a passed string parameter (our key word), uses STRFIND to
  459. see if the passed parameter is in the prompt_str variable, and then, in
  460. effect, replaces the calling statement with the resulting integer,
  461. indicating found (1) or not found (0). We can place this FUNCTION in our
  462. script, and call it by using :
  463.  
  464. CheckPrompt(string)
  465.  
  466. On big advantage which this FUNCTION offers our script is easy
  467. modification to include non-standard PCBoard prompts. Suppose that you
  468. try a new board, and it does not use the string "Password (Dots will
  469. echo)?" to ask for your password, but rather, merely says "Password?".
  470. Using the CheckPrompt function, all you have to do is add the new key
  471. word to the end of the old ELSEIF, and recompile the script, as in :
  472.  
  473.    elseif CheckPrompt("Password (Dots") || CheckPrompt("Password?")
  474.  
  475. rather than adding an entirely new ELSEIF statement to the script, and
  476. new lines to send the proper response, in duplication of what is already
  477. in the script.
  478.  
  479. Our script would then look like this, if we combined statements using
  480. the || operator......
  481.  
  482.  
  483. ; PCBLOG.WAS v.5.2 - 03/23/93
  484. ; Copyright (c) 1993, Gregg Hommel, All Rights Reserved
  485.  
  486. integer watchfor, lang = 0, graph = 0
  487. string prompt_str
  488.  
  489. proc main
  490.    when target 0 "?" call get_prompt
  491.    watchfor = 1
  492.    while watchfor
  493.    endwhile
  494.    exit
  495. endproc
  496.  
  497. proc get_prompt
  498.    termgets $ROW 0 prompt_str $COL
  499.    if chk_prompt("Command")
  500.       watchfor = 0
  501.  
  502.    ; combine the next two lines into one line before compiling
  503.    elseif chk_prompt("Enter)=yes?") || chk_prompt("More?")
  504.                      || chk_prompt ("Enter = Yes?")
  505.  
  506.       transmit "N^M"
  507.  
  508.    elseif chk_prompt("=no change?") && lang == 0
  509.          transmit "^M"
  510.          lang++
  511.  
  512.    ; combine the next two lines into one line before compiling
  513.    elseif chk_prompt("Enter)=no?") || chk_prompt("continue?")
  514.                  || chk_prompt("=none?") || chk_prompt("Enter = No?")
  515.  
  516.       if chk_prompt("graphics") || chk_prompt("Color?") && graph == 0
  517.          transmit "N Q NS^M"
  518.          graph++
  519.       else
  520.          transmit "^M"
  521.       endif
  522.    elseif chk_prompt("first name")
  523.       transmit $USERID
  524.       transmit "^M"
  525.    elseif chk_prompt("Password (Dots") || chk_prompt("Password?")
  526.       transmit $PASSWORD
  527.       transmit "^M"
  528.    elseif chk_prompt("new user?") || chk_prompt("new caller?")
  529.       transmit "r^M"
  530.    endif
  531. endproc
  532.  
  533. func chk_prompt:integer
  534.    strparm chk_out
  535.    strfind prompt_str chk_out
  536.    return FOUND
  537. endfunc
  538.  
  539.  
  540. OK, so I tried to slip a couple past you, without explaining them <GG>
  541.  
  542. # 1 - If line noise, etc. interferes with your log on, in most cases, it
  543. does little more than cause a resend of the prompt. However, in one
  544. case, it results in a different prompt being sent, and that is if line
  545. noise interferes when you send your name.
  546.  
  547. If PCBoard does not recognize the name, it asks if you want to continue
  548. as a new user, or resend the name. Since we want to resend the name,
  549. that is what we respond to the key word for that prompt.
  550.  
  551. # 2 - As we noted earlier, the graphics prompt generally contains one of
  552. two key words, and we tested for these in earlier versions of this
  553. script. However, there is one other "fact" about the graphics prompt...
  554. it also has the key words "(Enter)=no?", which we have just added to our
  555. script. The problem is, we don't want to simply reply with an enter, as
  556. we want to take advantage of the quick log on commands at the graphics
  557. prompt.
  558.  
  559. So... after testing for "Enter)=no?", we add another test, this time
  560. looking for "graphics" or "Color". If we find one those keywords along
  561. with the "Enter)=no?" key word, we send the quick log commands. If not,
  562. we send a simple return, and we are now able to vary our response based
  563. on finding MORE THAN ONE key word in a prompt_str. A further addition to
  564. this "checking" section is to use a "counter" in it (above we are using
  565. the variable, graph). On some PCBoard systems, the prompt for graphics
  566. actually contains two "?", and we use this "counter" to ensure that we
  567. only send a single response (since the second "?" would look, to the
  568. script, like a second prompt, and it would respond again, without the
  569. "counter").
  570.  
  571. This will be of use next posting, when we attempt to go beyond the "Main
  572. Board Command?" prompt, and into a mail door.
  573.  
  574. # 3 - I slipped another "counter" into the response to the language
  575. prompt. On some PCBoard systems, one of the languages offered includes a
  576. "?" (i.e. on CRS, one language is listed as "6 - Canadian, eh?" <GG>),
  577. and this "counter" (the variable, lang) does the same thing here as the
  578. "counter" in the graphics prompt does... makes sure that we only send a
  579. single response to the prompt, even if the script spots another "?"
  580. before it appears.
  581.  
  582.  
  583. Using INI files - why and how
  584. -----------------------------
  585.  
  586. One of the nicest aspects of Windows is the ability of most languages
  587. under it to access data stored in INI file format. Windows Aspect is no
  588. exception in this case.... it can easily read and write INI format
  589. files.
  590.  
  591. But why do I think these files are so great??
  592.  
  593. One of the best ways to handle varying configuration and other
  594. information, is to store it in a file, which is read by your application
  595. at start up, and allows the application to control various items based
  596. on the information contained therein. However, on big drawback to this
  597. is that the application has to read the data from the "configuration"
  598. file, and then be able to parse the data it has read into useful
  599. information. All of this takes time, and is quite subject to error (i.e.
  600. a misplaced space can totally destroy the parsing of a text string into
  601. usable information.)
  602.  
  603. The INI file format simplifies things. Basically, the INI format
  604. consists of a "section" header, contained in square brackets ( [ ] ),
  605. followed by lines of text in the format
  606.  
  607. description=information
  608.  
  609. As a result, there is less chance of errors occurring during editing,
  610. since, in order to read the information, no parsing of text need occur.
  611. Each line can be a single bit of information for the application to use,
  612. and that data need not be retrieved from the configuration file unless
  613. and until needed by the application (since it is not part of a larger
  614. text string). Furthermore, the "description" portion of each line can be
  615. set such that it is fairly easily understood by someone when editing the
  616. INI file in a standard text editor. As example, if we wanted to store
  617. our User ID for a system, we could do so like this :
  618.  
  619. UserID=Gregg Hommel
  620.  
  621. which, since it is so clearly described, is difficult to "blow" when
  622. editing an INI file manually. To further explain how simple things can
  623. be, let's create a file, PCBLOG.INI, in our \WINDOWS subdirectory, using
  624. NotePad or NDW's DeskEdit or similar.
  625.  
  626. Now, the reason we want it in the \WINDOWS subdirectory is quite
  627. simple.... in most languages, if there is no path specified for a file
  628. with an INI extension, it is assumed to be in the \WINDOWS subdirectory.
  629. This makes accessing it in a script much simpler (for now... later,
  630. we'll move it elsewhere, but for a start, we'll put it where Wasp
  631. expects to find it), and is in itself, another reason why I like using
  632. INI files... they can be easy to set up and access, since they are
  633. expected to be in that one place unless a different location is
  634. specified.
  635.  
  636. Now, let's create our INI similar to this :
  637.  
  638. [Canada Remote Systems]
  639. UserID=Gregg Hommel
  640. Password=guesswho
  641. UseLanguage=6
  642. Graphics=N
  643. MailCmd=open 67
  644.  
  645. [PC Board Home]
  646. UserID=greggy
  647. Password=whocares
  648. UseLanguage=1
  649. Graphics=Y
  650. MailCmd=open 2
  651.  
  652. Replace the above with appropriate information for your PCBoard systems,
  653. of course, but you get the idea. There can be several "sections", each
  654. one marked by a header in square brackets.
  655.  
  656. To explain.... the heading (in square brackets) is the "name" you have
  657. assigned to a system in your dialing directory (the reason for this will
  658. become obvious later). The rest of the lines are fairly straight
  659. forward, based on their descriptions, and will be used to extend our log
  660. on script so that it responds differently, depending on the board you
  661. are connected to, with the last line being used to extend our script
  662. beyond the "Main Board Command?" prompt by opening the mail door for
  663. that system.
  664.  
  665. But why are we putting the name and password here when they are already
  666. in the dialing directory entry? Well, two reasons, really... 1) I needed
  667. some examples to show how an INI can be used <G>, and 2) the fields for
  668. entries in the dialing directory are limited in size, and many people
  669. have told me that their name, or system ID will not fit in the field,
  670. because it is too long (making UserID a prime candidate to use as an
  671. example).
  672.  
  673. Using the INI information in our script
  674. ---------------------------------------
  675.  
  676. The idea behind using an INI file to store various data for the systems
  677. that we use our PCBLOG script with, is so that they need not all be
  678. treated exactly the same way during a log on. One system may be best
  679. used without graphics (like me, the sysop can't draw a straight line
  680. without help! <GG>), while another may have the greatest ANSI art during
  681. log on that you have ever seen. Using an INI file to store data for the
  682. various systems makes it easy to customize a log on so that it may be
  683. different depending on the system being accessed.
  684.  
  685. The first thing we have to do is figure out how to access the
  686. information in the INI from a script, and this is done through the Wasp
  687. command, PROFILERD. (Guess what? Some languages refer to an INI file as
  688. a PROFILE data file. Does the command make more sense now?? <GG>)
  689.  
  690. Actually, there are two such commands in Wasp, PROFILERD and PROFILEWR.
  691. I'm sure that I needn't point out that one is used to read from an INI
  692. file, the other is used to write to one <G> In either case, the format
  693. of the command syntax is similar :
  694.  
  695. PROFILERD ININame Section description variable
  696.  
  697. The variable here can be either text or an integer. For purposes of our
  698. script, we are going to use only text variables, but one could as easily
  699. read an integer from an INI file.
  700.  
  701. As example, let's assign the string variable user_id for our name on any
  702. given system. This would make the command to read that data for CRS
  703. above as :
  704.  
  705. PROFILERD "pcblog.ini" "Canada Remote Systems" "UserID" user_id
  706.  
  707. Once done, any normal manipulation of the variable thus determined can
  708. be carried out. Also, any of the above quoted strings can be replaced
  709. with a variable, including various system variables. And now you can see
  710. why I wanted the section heading to be identical to the name for the
  711. system that you have in your dialing directory... that name is
  712. represented by the system variable $D_NAME, which can be used in the
  713. above command, as so....
  714.  
  715. PROFILERD "pcblog.ini" $D_NAME "UserID" user_id
  716.  
  717. This, of course, means that the script would read the data for whatever
  718. system we have dialed and connected to. IOW, the script will
  719. automatically read the information it needs from the correct section of
  720. the INI file, based on the dialing directory name for whatever system we
  721. are connected to while the script is running.
  722.  
  723. To use this in our PCBLOG.WAS file, we need do a couple of things....
  724.  
  725. 1) at the start of the script, before the main procedure, modify the
  726. string global declaration to read :
  727.  
  728. string prompt_str, ini = "PCBLOG.INI", user_id
  729.  
  730. 2) In the main procedure, after the line which sets the WHEN TARGET, add
  731. the following line :
  732.  
  733.    set dialdir access $DIALENTRY
  734.    profilerd ini $D_NAME "UserID" user_id
  735.  
  736. (What this does is to make sure that there is a value for the system
  737. variable $D_NAME based upon the last dial directory entry accessed, i.e.
  738. the dialing directory entry which we used to connect to this system, and
  739. then reads the INI for our user name into the global variable, user_id)
  740.  
  741. 3) Modify our proc get_prompt section dealing with sending our name from
  742.  
  743.       transmit $USERID
  744.  
  745. to :
  746.  
  747.       transmit user_id
  748.  
  749. and our script would send the correct name for whatever system we are
  750. logged on to.
  751.  
  752. We needn't go through each change for this, so here is our modified
  753. PCBLOG.WAS script.....
  754.  
  755.  
  756. ; PCBLOG.WAS v.5.2a - 03/24/93
  757. ; Copyright (c) 1993, Gregg Hommel, All Rights Reserved
  758.  
  759. integer watchfor, lang = 0, graph = 0
  760. string prompt_str, ini = "PCBLOG.INI"
  761. string user_id, p_word, lang_num, graph_say
  762.  
  763. proc main
  764.    when target 0 "?" call get_prompt
  765.    set dialdir access $DIALENTRY
  766.    profilerd ini $D_NAME "UseLanguage" lang_num
  767.    profilerd ini $D_NAME "Graphics" graph_say
  768.    profilerd ini $D_NAME "UserID" user_id
  769.    profilerd ini $D_NAME "Password" p_word
  770.    watchfor = 1
  771.    while watchfor
  772.    endwhile
  773.    exit
  774. endproc
  775.  
  776. proc get_prompt
  777.    termgets $ROW 0 prompt_str $COL
  778.    if chk_prompt("Command")
  779.       watchfor = 0
  780.    elseif chk_prompt("Enter)=yes?") || chk_prompt("More?") \
  781. || chk_prompt ("Enter = Yes?")
  782.       transmit "N^M"
  783.    elseif chk_prompt("=no change?") && lang == 0
  784.       transmit lang_num
  785.       transmit "^M"
  786.          lang++
  787.    elseif chk_prompt("Enter)=no?") || chk_prompt("continue?") \
  788. || chk_prompt("=none?") || chk_prompt("Enter = No?")
  789.       if chk_prompt("graphics") || chk_prompt("Color?") && graph == 0
  790.          transmit graph_say
  791.          transmit " Q NS^M"
  792.          graph++
  793.       else
  794.          transmit "^M"
  795.       endif
  796.    elseif chk_prompt("first name")
  797.       transmit user_id
  798.       transmit "^M"
  799.    elseif chk_prompt("Password (Dots") || chk_prompt("Password?")
  800.       transmit p_word
  801.       transmit "^M"
  802.    elseif chk_prompt("new user?") || chk_prompt("new caller?")
  803.       transmit "r^M"
  804.    endif
  805. endproc
  806.  
  807. func chk_prompt:integer
  808.    strparm chk_out
  809.    strfind prompt_str chk_out
  810.    return FOUND
  811. endfunc
  812.  
  813.  
  814. Going beyond the "Main Board Command?" prompt
  815. ---------------------------------------------
  816.  
  817. If we add another global variable, and two additional lines to the
  818. script, we can carry things one step beyond the "Main Board Command?"
  819. prompt, and one step closer to the script being a mail management
  820. utility. We need the variable mail_cmd declared as a global string, and
  821. the following lines added :
  822.  
  823. 1) In the main procedure, prior to watchfor = 1, add :
  824.  
  825.    profilerd ini $D_NAME "MailCmd" mail_cmd
  826.  
  827. 2) In procedure get_prompt, change the first section after the termgets
  828. to :
  829.  
  830.    if chk_prompt("Command")
  831.       transmit mail_cmd
  832.       transmit "^M"
  833.       watchfor = 0
  834.  
  835. Thus, when the "Main Board Command?" prompt is received, our script will
  836. send what we have recorded in the INI file as the command needed to open
  837. the mail door on that system.
  838.  
  839.  
  840. Using multi-tasking to perform multiple routines
  841. ------------------------------------------------
  842.  
  843. Actually, I apologize.... I have decided that this is more appropriate
  844. in a later "lesson", after we have first built some additional script
  845. files to prepare for managing the mail and multi-tasked activity.
  846.  
  847. I also have re-arranged two lessons, # 4 and # 5, as it seems to make
  848. more sense to discuss dialog boxes, and writing a "set up" script for
  849. our mail handler, before actually working on the mail handling itself.
  850.  
  851. So... although we have already taken a quick first look at using INI
  852. files, in the next "lesson", we'll discuss them further as they apply to
  853. storing data which we'll want when doing a mail run on a given system
  854. later.
  855.  
  856. More discussion of INI files
  857. ----------------------------
  858.  
  859. We have already discussed using an INI file to store basic information
  860. about the systems which we may want to use PCBLOG with, however, an INI
  861. file can be far more useful than this "basic" data.
  862.  
  863. When we extend our PCBLOG script into PCBMAIL, there is quite a bit of
  864. information which we may want to have available for handling the mail,
  865. and which may change from system to system. Things like the "root" for
  866. any mail packet for that board, where we might want to store the mail
  867. from that system, and how we may want to rename it to avoid the PCP/Win
  868. Zmodem problems which occur when there is already a file in existence in
  869. your download directory.
  870.  
  871. The most logical place for all of this information IF you are using
  872. simply one system for your mail, is as string variables or macros
  873. defined in the script. However, if you intend using the same script for
  874. more than one system, and do not want to have to redefine variables, and
  875. recompile the script under a different name, multiple times, the logical
  876. place for this "variable" data is in an INI file, where a "generic"
  877. script can read different values for variables depending upon the system
  878. logged on to.
  879.  
  880. Thus, we can use our INI file from our earlier PCBLOG script, to store
  881. much more information, information which can be variable from system to
  882. system. However, since we are now going to extend our script into mail
  883. handling, let's rename it as PCBMAIL.WAS for simplicity sake, and rename
  884. our INI file as PCBMAIL.INI.
  885.  
  886. Now, the information which we may need in PCBMAIL.INI is a little more
  887. extensive than that in PCBLOG.INI. Let's see what it might be....
  888.  
  889. 1) Every system uses a different "root" name for it's mail packets. One
  890. system which I use sends me CRS.QWK, while another sends PROPC.QWK. So
  891. one of the first items needed in our INI would be the "root" name of a
  892. QWK or REP file for a given system. For this information, we can use the
  893. simple line :
  894.  
  895. board_ID=
  896.  
  897. 2) We already have an entry for the command necessary to open the mail
  898. door (from PCBLOG.INI), since this also varies from system to system.
  899. This is :
  900.  
  901. door_ID=
  902.  
  903. 3) To avoid problems caused by the implementation of Zmodem used in
  904. PCP/Win (since Zmodem is by far the most common form of QWK file
  905. transfer), we should move our mail packets out of our download
  906. directory, and into a storage directory. This allows us to take our time
  907. reading a QWK, since we need not worry about it being over-written by
  908. the next downloaded QWK. So, we would want to store the directory
  909. information where we might move the QWK to, in our INI. For this, the
  910. line
  911.  
  912. mail_dir=
  913.  
  914. will suffice.
  915.  
  916. 4) If the script moves a new QWK into our mail storage directory while
  917. we still have an older QWK there, we have not eliminated the Zmodem
  918. over-write problem, merely changed when it occurs (i.e. when we move the
  919. mail to the storage directory). To eliminate it totally, we should also
  920. rename our mail packets in some fashion as to avoid any possibility of
  921. overwriting older packets. The simplest form of this is to simply change
  922. the last character of the extension, starting at A, until Z.
  923.  
  924. However, on some systems, especially those with a lot of mail, you may
  925. keep quite a few older packets while you finish reading them. For this
  926. purpose, perhaps you might want to also incorporate the date of
  927. downloading into the renaming routine, so that you could easily tell at
  928. a glance when that file was received.
  929.  
  930. Let's therefore assume that we may want some system packets renamed in
  931. the simplest way, and other system's mail renamed to include a date. So,
  932. we need to store the renaming method in our INI also, in this line :
  933.  
  934. rename_as=
  935.  
  936. Do you perhaps begin to see how important and simple an INI file can be
  937. when used in conjunction with a Wasp script? Since the format is
  938. predefined, and there are script commands to simply read the data stored
  939. there (actually, a one line script command will read any single data
  940. item in an INI file), configuration information, and variable data can
  941. be easily saved and read from an INI file, without need to parse text,
  942. or any other method of determining precisely which portion of a file is
  943. the one we want here.
  944.  
  945.  
  946. Dialog Boxes - getting user input
  947. ---------------------------------
  948.  
  949. So, now we have a script which can read an INI file for variable data
  950. for each board (even if it does not yet read or use all of the
  951. information that we have decided to put in the INI <G>), and log on to a
  952. system based on that data. But... we created the INI file through a text
  953. editor. Now what we are going to do is to write a script which can be
  954. used to create that information in the INI file, and for that, we will
  955. need and use our first simple dialog box.
  956.  
  957. To do this, let's look at the PCP/Win Dialog Editor.
  958.  
  959. In a lot of languages (including DOS Aspect), you would have to design a
  960. dialog box "the hard way"... using various commands for components of
  961. the dialog box, and sort of guessing at the "position" information
  962. needed. Then, you would test compile the script to make sure that
  963. everything was where you wanted, and if not, edit the source, and try
  964. again. ProComm Plus for Windows makes this all much easier, through the
  965. Dialog Editor.
  966.  
  967. Basically, this is a drawing programme, but instead of drawing pictures,
  968. we use it to "draw" dialog boxes for our scripts. No need to guess at x
  969. and y coordinates... simply draw the main box, add the components you
  970. want, stretch them or shrink them to fit and position them where you
  971. want them, and save the code as a WUD extension file which can be copied
  972. into your source script.
  973.  
  974. There are several points to be cautious of, however. The biggest is
  975. resolution. Always remember that, although you have the perfect design
  976. for your dialog box, either you, or someone else, will, at some point in
  977. time, run the script when the video resolution is different from what it
  978. was when you designed the dialog. Text which fits in an edit box at
  979. 1024x768 may be barely legible at 640x480. Those nice, tight radio
  980. buttons at the bottom of the dialog at 640x480 may be spread all over
  981. the screen at 1024x768.
  982.  
  983. So...... ALWAYS check any dialog designed for a script with all
  984. resolutions which you or others may be expected to run!
  985.  
  986. That being said, let's look at a "sample" segment of code for a dialog
  987. box. Don't worry about what the variables are, or what will appear in
  988. the dialog... for now, we are merely interested in discussing the
  989. make-up of the code. (Actually, this is roughly the code we will use
  990. when we write the script which we'll use to set parameters for our mail
  991. manager script, but for now, the contents of the various controls are
  992. unimportant.)
  993.  
  994. If you'd like some practice using the Dialog Box Editor, try this....
  995.  
  996. 1) From this message, copy the code for the dialog box to the Windows
  997. ClipBoard.
  998.  
  999. 2) Open the PCP/Win Dialog Box Editor.
  1000.  
  1001. 3) From the menu of the Dialog Box Editor, select Edit.... Paste Dialog,
  1002. and the "generic" dialog box represented by this code should appear. If
  1003. you want, try clicking with the mouse in various spots. Notice how the
  1004. information box in the lower right changes information based on where
  1005. you click. Try double clicking on some of the controls to see the
  1006. options available for them. Don't worry, you won't hurt anything, since
  1007. all you will be working with is a "copy" of the original code. (we'll
  1008. discuss more about the Dialog Box Editor, and using the various windows
  1009. and options in a later lesson.)
  1010.  
  1011. But now, let's look at the code for our dialog box sample....
  1012.  
  1013. First some "rules"....
  1014.  
  1015. 1) The "generic" format for all dialog box commands is :
  1016.  
  1017.        command x-pos y-pos x-width y-height variables
  1018.  
  1019. 2) ALL dialog boxes MUST begin with the DIALOGBOX statement and end with
  1020. the ENDDIALOG statement.
  1021.  
  1022. 3) In Wasp, anything between these two statements is considered to be
  1023. part of the dialog box. Remember that! You can't put any if...endif
  1024. constructs between the dialogbox...enddialog statements (they are not
  1025. dialog box commands), nor can you put any assign statements, while..
  1026. endwhile sets, or anything else normally used in a script. There can be
  1027. nothing but dialog box commands between the dialogbox... enddialog
  1028. statements!
  1029.  
  1030. That said....
  1031.  
  1032. dialogbox 80 45 245 186 13
  1033.    groupbox 10 4 224 39 shadow
  1034.    text  18 12 209 28 center "Use this dialog box to set parameters."
  1035.    text  10 51 81 8 right "Select a PCBoard system :"
  1036.    combobox 100 49 135 55 pcblist name sort
  1037.    text  10 71 60 8 left "QWK filename?"
  1038.    editbox 75 69 45 12 board 8
  1039.    text  125 70 69 8 left "Mail door # (name)?"
  1040.    editbox 195 68 40 13 door
  1041.    text  10 91 60 8 left "Mail directory?"
  1042.    editbox 75 89 50 12 maildir
  1043.    text  130 91 60 8 left "Join Conference?"
  1044.    editbox 195 89 40 12 defconf
  1045.    groupbox 10 110 225 30 "Rename mail options"
  1046.    radiobutton 20 122 100 10 "Standard format - QWA" rb1
  1047.    radiobutton 130 122 100 10 "Alternate format - with date" endgroup
  1048.    iconbutton 10 147 iconvar pwfile icondx
  1049.    iconbutton 40 147 iconvar2 pwfile icondx2
  1050.    pushbutton 76 153 40 14 "&Save" normal
  1051.    pushbutton 134 153 41 14 "&Delete" normal
  1052.    pushbutton 195 153 40 14 "E&xit" cancel
  1053. enddialog
  1054.  
  1055. Let's take a look at some of the items above....
  1056.  
  1057. This dialog has the following "controls" in it....
  1058.  
  1059.   groupboxes, text (labels), a combobox, editboxes, radiobuttons,
  1060.   iconbuttons, and pushbuttons.
  1061.  
  1062. Groupboxes merely "outline" something, with no real "value" in the
  1063. dialog box. Generally, they are used to indicate "sets" of controls,
  1064. which have a related function, task, purpose, or setting. Basically, a
  1065. groupbox can be used to make a dialog look a little prettier, and in
  1066. most cases, be easier to use and/or read.
  1067.  
  1068. Text (labels) are exactly what they appear to be... text labels which
  1069. you can place anywhere in a dialog box to inform the user of something,
  1070. be it a description of the contents of another dialog control, or merely
  1071. some informative text.
  1072.  
  1073. Combobox... this is a little trickier, and can be quite useful. A
  1074. combobox will display a comma-delimited string variable as a list of
  1075. entries in a drop down box. It appears in the dialog box as a single
  1076. "line" box with a down arrow on the right hand side. Clicking on that
  1077. down arrow will result in a box appearing below the original one,
  1078. containing the contents of a defined, comma-delimited string, one
  1079. "entry" per line. The user can then select one of these entries, which
  1080. is stored in a second string variable. (There is also another control,
  1081. fcombobox, which behaves virtually identically, except that it gets the
  1082. "list" of entries from a file, rather than a string variable. This
  1083. allows for more entries, since string variables in Wasp are limited to
  1084. 255 characters.)
  1085.  
  1086. A combobox is an excellent method of offering a user a choice of items,
  1087. without taking up large amounts of space in the dialog box.
  1088.  
  1089. Editboxes allow for user input. The user can make text entries into an
  1090. editbox, which are then saved in a string variable. Editboxes are used
  1091. when the string to be entered is an unknown, which prevents offering a
  1092. list to the user.
  1093.  
  1094. Radiobuttons, iconbuttons, and pushbuttons are controls which allow
  1095. simple user choices, and can also be used to control the display of the
  1096. dialog box. Radiobutton sets (and they must be in a set of at least two
  1097. buttons), allow the user to make a choice of ONE of several options. His
  1098. choice is mutually exclusive of the other options in the radiobutton
  1099. set, i.e. only one of any set of radiobuttons can be the "active" one at
  1100. any given time.
  1101.  
  1102. Iconbuttons and pushbuttons can operate similarly. The only real
  1103. difference is whether or not an icon is displayed on the button, with
  1104. text below it, or the text is displayed right on the button face. Both
  1105. iconbuttons and pushbuttons can be tested to determine which one was
  1106. activated, and some script action performed based on that. One other
  1107. "big" difference is that a "normal" or "cancel" type of pushbutton, when
  1108. depressed, will normally cause the destruction of the dialog box on the
  1109. screen. If this is not desired, then one can use an "update" pushbutton.
  1110.  
  1111. So.... what we have above is a relatively simple dialog box for
  1112. obtaining various user input items for the extension of our PCBLOG
  1113. script into a mail handler script. If you paste the above dialog box
  1114. commands into the dialog box editor, you will be able to "see" what the
  1115. dialog box will look like to our user. This procedure, and the commands
  1116. which make up a dialog box in Wasp are far simpler to use, and much more
  1117. powerful than the commands available to DOS Aspect programmers. The
  1118. dialog box editor allows you to "create" your dialogs basically by
  1119. "drawing" them on screen, and then saving the code. As contrast, in DOS
  1120. Aspect, you would write the code, then compile the script to see how the
  1121. box appears on screen. If it isn't the way you want it, then you would
  1122. need rewrite the code, etc. to get it right. In Wasp, the dialog can be
  1123. "designed" to appear correctly before any code is even saved, and that
  1124. code need not even be written, as the dialog box editor will write it
  1125. for you, based on the designed dialog box on the screen of the editor.
  1126.  
  1127. One basic to remember about dialog box items is that most of them have
  1128. TWO identifications available. By far the more important identification
  1129. of a dialog box item is it's "value". It is this entry which can be used
  1130. to determine what action has occurred in a dialog box, and thus, what we
  1131. want to happen because of it. Various "types" of controls have different
  1132. values, and each control of that type which is in the dialog has a
  1133. unique value based on that.
  1134.  
  1135. As example, let's look at radiobuttons... these have a value of 50 for
  1136. the first group of buttons to appear in the dialog box (when I say
  1137. "appear", I am not referring to on screen appearance, but the
  1138. "appearance" of the radiobutton in the list of dialogbox statements).
  1139. The second group would have a value of 51, the third, 52, etc. Using a
  1140. switch/case construct, we could thus test for a value of $DIALOG which
  1141. matches these, and thus, determine which group of radiobuttons the user
  1142. activated. The "value" for this identification is listed in the
  1143. definition of each dialog box command in the Wasp manual.
  1144.  
  1145. The second identification is a "group" identification number, which is
  1146. used to identify ALL dialog box items of that "type". This
  1147. identification is used when one wants to update a dialog to show changes
  1148. in data it displays, say, based on the selection of a radiobutton. To
  1149. continue with our example from above, the identification integer used
  1150. for ALL radiobuttons in a dialog, with the UPDATEDLG command would be a
  1151. 2. The values for this identification are listed in the definition of
  1152. the UPDATEDLG command in the Wasp manual.
  1153.  
  1154. Now having to search back and forth in the manual to determine the value
  1155. to be used for each type of command, and for each type of action for
  1156. that command, can get confusing (I know it did for me, at first <G>), so
  1157. I created this "table" summarizing the maximum number of each control
  1158. which can be used in a dialog box, and the two values for each type of
  1159. dialog box command. I have several printouts of the table posted around
  1160. my desk so that I can easily check any value which I need....
  1161.  
  1162. -----------------------------------------------------------------
  1163. DIALOG ITEM    # of items       CONTROL ID     UPDATEDLG VALUE
  1164. -----------------------------------------------------------------
  1165. CHECKBOX           32           70 + index             1
  1166. COMBOBOX           16          170 + index           512
  1167. DIRLISTBOX          1             150               1024
  1168. DIRPATH             1
  1169. EDITBOX            16          230 + index           128
  1170. FCOMBOBOX           4          190 + index            32
  1171. FEDITBOX            1             250                  8
  1172. FLISTBOX            4          130 + index            16
  1173. FTEXT               1                                  4
  1174. GROUPBOX           16
  1175. ICON               16                               2048
  1176. ICONBUTTON         16          210 + index          4096
  1177. LISTBOX             8          110 + index           256
  1178. PUSHBUTTON *
  1179. normal             16           10 + index
  1180. cancel              1              1
  1181. update             16           30 + index
  1182. RADIOBUTTON **      8 (16)      50 + index             2
  1183. VTEXT              16                                 64
  1184.  
  1185. * - there are three different types of pushbuttons, normal, update and
  1186. cancel, each with different CONTROL ID values.
  1187.  
  1188. ** - there can be a maximum of 8 groups of radiobuttons, each with up to
  1189. 16 radiobuttons in the group.
  1190.  
  1191. Any DIALOGBOX item without a CONTROL ID entry indicates that it can
  1192. simply be placed in the dialog box, as a passive control, and can not be
  1193. acted upon in a script. Any item without an entry in the UPDATEDLG
  1194. column indicates that updating of the item is either not possible or
  1195. automatic. Furthermore, some items do not require updating, if they are
  1196. the currently active control, i.e. a combobox will update itself based
  1197. upon the user selection when it is made. One would use the UPDATEDLG
  1198. value for these items only if the script were what changed the value of
  1199. any variables, rather than user interaction.
  1200.  
  1201. This time, we're going do things a little differently than we have done
  1202. in earlier lessons. Rather than constantly repeating lines of code
  1203. segments as we "build" our script, and since I already have a script
  1204. written to perform the tasks which we need to store data in the INI file
  1205. for our PCBMail script, what we'll do is post the script in it's
  1206. entirety, with comments (indicated in the normal script fashion, by a
  1207. ";" preceding each line) inserted where appropriate to explain what is
  1208. being done. You may, if you wish, cut and paste from these lessons (with
  1209. or without comments included) in order to "create" the complete
  1210. SETMAIL.WAS script....
  1211.  
  1212. ***********************************************************************
  1213.  
  1214. ;SETMAIL.WAS v.5.2a - 04/13/93 011:40 AM
  1215. ;Copyright (c) 1993, Gregg Hommel, All Rights Reserved
  1216.  
  1217. ; SETMAIL.WAS is a Windows Aspect script for use with ProComm Plus for
  1218. ; Windows 1.01. It is used to set various parameters into an INI file.
  1219. ; PCBMail can then read these parameters to set options when it is run.
  1220.  
  1221. integer flag = 0, icondx = 5, icondx2 = 41, ndx = 0, rb1 = 1, source = 0
  1222. string board, defconf, dldir, door, iconvar = "UserID"
  1223. string iconvar2 = "Password", ini = "PCBMAIL.INI", maildir
  1224. string name = "(None)", pcblist = "(None)", pwfile = $PWTASKPATH, pword
  1225. string ren_def, user
  1226.  
  1227. ; the above list of global variable declarations shows a personal
  1228. ; preference on my part... they are declared alphabetically. I have
  1229. ; found that this makes it far easier to add a variable later, without
  1230. ; being concerned about duplicating a previously used one. Furthermore,
  1231. ; and I have no proof of this, other than my opinion, but I have found
  1232. ; that my scripts seem to run "better" with my variables declared in
  1233. ; alphabetical order.
  1234. ; It also points out a fact not clearly stated in the manuals... when a
  1235. ; global variable is declared, a default value can be assigned to it
  1236. ; then. If the default value of the variable is a PCP/Win internal
  1237. ; system variable, this too can be assigned when the variable is
  1238. ; declared.
  1239.  
  1240. proc main
  1241.    integer button, charval, dlgstatus, lendx
  1242.    string del_set, pword2, save_set, user2, wrong
  1243.  
  1244. ; In our dialog box later, we will need to have a global string which is
  1245. ; the name and path of the PCP/Win executable (which is where we will
  1246. ; find the icons that we'll use <G>). We already have the variable
  1247. ; pwfile assigned the name of the default path for the PCP/Win
  1248. ; directory, i.e. $PWTASKPATH, so now we just use the ADDFILENAME
  1249. ; command to add PW.EXE to that path.
  1250.  
  1251.    addfilename pwfile "PW.EXE"
  1252.  
  1253. ; In many cases, a script may be performing some function or task, but
  1254. ; show nothing visible on the screen to indicate this to a user of that
  1255. ; script. The following dialog box is placed here simply to tell a user
  1256. ; that the script is indeed running. You may find it necessary to insert
  1257. ; dialogs of this type in your scripts, so as to remind yourself that,
  1258. ; although nothing appears to be happening on the screen, in reality,
  1259. ; the script is doing things.
  1260.  
  1261.    dialogbox 100 75 200 30 13
  1262.       text 10 10 180 10 center "PCBMAIL setup is scanning for boards 
  1263. now."
  1264.    enddialog
  1265.  
  1266. ; It would be quite simple to create a string listing the names of the
  1267. ; systems which you call, to be used in our combobox later. However,
  1268. ; once again, we are trying to create a "generic" script which can be
  1269. ; used by anyone. When doing so, we have to remember that the user's
  1270. ; dialing directory might change from time to time. To account for this,
  1271. ; and to make the script as "generic" as possible, we are going to read
  1272. ; each entry in the dialing directory, and if the user has set the
  1273. ; script for that entry as PCBMAIL, we will get the name of the entry,
  1274. ; and add it to our string variable, pcblist, which will be used in our
  1275. ; combobox later. To do this, we use the internal "system" variables,
  1276. ; $DIALCOUNT (the number of entries in the currently active dialing
  1277. ; directory), $D_SCRIPT (the entry in the last accessed dialing
  1278. ; directory entry which lists the script to use with it), and $D_NAME
  1279. ; (the name used in the dialing directory for the last accessed entry).
  1280.  
  1281.    for ndx = 1 upto $DIALCOUNT
  1282.       set dialdir access ndx
  1283.       if strcmpi $D_SCRIPT "pcbmail"
  1284.          strcat pcblist ","
  1285.          strcat pcblist $D_NAME
  1286.       endif
  1287.    endfor
  1288.  
  1289. ; SETMAIL uses a section on the INI for board "(None)" to show sample
  1290. ; settings in the dialog box. However, if this is the first time that
  1291. ; SETMAIL has been run, or if the INI has been deleted, there will be no
  1292. ; section, labeled [(None)]. The easiest way to determine if a section
  1293. ; exists in an INI file, is to read a description that definitely has an
  1294. ; entry, if that section exists. If it is a null string (i.e. if there
  1295. ; is no entry there, then you can safely assume that the section itself
  1296. ; doesn't exist. To this end, we read a description which we know should
  1297. ; exist in [(None)], and if we get a null string, we assume that the
  1298. ; section doesn't exist, and we "create" it.
  1299. ; Note here a couple of points... if a named INI file does not exist,
  1300. ; the first PROFILEWR command issued to that INI name will create the
  1301. ; INI file. Second, if a section in a given INI file does not exist, the
  1302. ; first PROFILEWR command issued which writes to that section will also
  1303. ; automatically add the appropriate section header. This makes using INI
  1304. ; files quite easy, as you do not need to create them, or open them for
  1305. ; reading or writing, or any of the other "normal" file manipulation
  1306. ; needs.
  1307.  
  1308.    profilerd ini name "board_id" board
  1309.    if null_str(board)     ; if there is no (None) entry for here
  1310.       board = "chanone"   ; set default values for it, and write
  1311.       door = "e.g. 15"    ; them to the INI file
  1312.       maildir = "X:\MAILDIR"
  1313.       defconf = "e.g. 2"
  1314.       write_ini()
  1315.    else                   ; otherwise
  1316.       read_ini()          ; read the default values for (None)
  1317.    endif
  1318.    pause 1
  1319.    destroydlg
  1320.  
  1321. ; Once we have scanned the dialing directory for a list of entries which
  1322. ; are pre-set to use the PCBMAIL script, we then check to see if there
  1323. ; were any! If the pcblist variable contains nothing but the entry
  1324. ; (None) (put there by default), then there are no PCBMAIL systems in
  1325. ; this directory, and we so inform the user, and then shut down this
  1326. ; script.
  1327.  
  1328.    if strcmpi pcblist "(None)"
  1329.       errormsg "There are no PCBMAIL systems in the dialing directory!"
  1330.       exit
  1331.    endif
  1332.  
  1333. ; this next set of code just may look a little familiar <G> This is the
  1334. ; dialog box which we worked with in the previous lesson, now part of
  1335. ; the script it was originally intended for.
  1336.  
  1337.    dialogbox 80 45 245 186 13
  1338.       groupbox 10 4 224 39 shadow
  1339.       text  18 12 209 28 center "Use this dialog box to set the \
  1340. parameters which the PCBMAIL script needs to operate. The Join \
  1341. Conference? parameter is optional."
  1342.       text  10 51 81 8 right "Select a PCBoard system :"
  1343.       combobox 100 49 135 55 pcblist name sort
  1344.       text  10 71 60 8 left "QWK filename?"
  1345.       editbox 75 69 45 12 board 8
  1346.       text  125 70 69 8 left "Mail door # (name)?"
  1347.       editbox 195 68 40 13 door
  1348.       text  10 91 60 8 left "Mail directory?"
  1349.       editbox 75 89 50 12 maildir
  1350.       text  130 91 60 8 left "Join Conference?"
  1351.       editbox 195 89 40 12 defconf
  1352.       groupbox 10 110 225 30 "Rename mail options"
  1353.       radiobutton 20 122 100 10 "Standard format - QWA" rb1
  1354.       radiobutton 130 122 100 10 "Alternate format - with date" endgroup
  1355.       iconbutton 10 147 iconvar pwfile icondx 
  1356.       iconbutton 40 147 iconvar2 pwfile icondx2 
  1357.       pushbutton 76 153 40 14 "&Save" normal
  1358.       pushbutton 134 153 41 14 "&Delete" normal
  1359.       pushbutton 195 153 40 14 "E&xit" cancel
  1360.    enddialog
  1361.  
  1362. ; if you recall our previous discussion of the identification integers
  1363. ; assigned to the various elements of a dialog box, and the table which
  1364. ; we "created" during that discussion, you will now see the first
  1365. ; implementation of using those identification integers, in particular,
  1366. ; the column labeled CONTROL ID.
  1367. ; When the dialog above first appears, the item "(None)" is the default
  1368. ; selection in the combobox. We have read the default "sample" values
  1369. ; from the INI for (None), and want them displayed, however, we do not
  1370. ; want a user to edit these, since they are only sample values. As a
  1371. ; result, what we want to do is to display those values but disable the
  1372. ; various dialog box elements so that the user can't use them. Once he
  1373. ; has selected a system from the combobox listing, we will re-enable
  1374. ; various controls, but for now, we will use the CONTROL ID integers
  1375. ; from our DIALOG ITEM table to disable some of our dialog.
  1376.  
  1377. ; disable the editboxes
  1378.    disable ctrl 230 233
  1379.  
  1380. ; disable the iconbuttons
  1381.    disable ctrl 210 211
  1382.  
  1383. ;disable the one group of radiobuttons
  1384.    disable ctrl 50
  1385.  
  1386. ; disable the first two pushbuttons
  1387.    disable ctrl 10 11
  1388.  
  1389. ; use the value of $DIALOG to see if anything in the dialog box has been
  1390. ; modified, and act accordingly
  1391.  
  1392.    dlgstatus = $DIALOG
  1393.    while dlgstatus != 1
  1394.       switch dlgstatus
  1395.  
  1396. ; based upon the CONTROL ID entry in our table, case 170 indicates that
  1397. ; the combobox has been selected in the dialog box, and we thus, handle
  1398. ; the selection of a name from that drop down box combobox. One thing we
  1399. ; have to remember... it is possible that a user might first select a
  1400. ; system from the list, and then later, re-select the (None) entry. Our
  1401. ; code has to handle this contingency, and disable controls again if
  1402. ; (None) is the selected entry.
  1403.  
  1404. ; Here also, we see the first use of the other column of entries in our
  1405. ; DIALOG ITEM identification table, i.e. the UPDATEDLG VALUE column.
  1406. ; What this does is quite simple... once you have changed, via the
  1407. ; script, the values for any variables on screen in a dialog box, they
  1408. ; are not changed on screen automatically. Your script has to issue an
  1409. ; UPDATEDLG command, indicating which controls are to be updated on the
  1410. ; screen. The integer value issued with the UPDATEDLG command is the sum
  1411. ; of the individual integer UPDATEDLG VALUE entries from our table.
  1412. ; There is one other "value" which can be used, that being a -1, which
  1413. ; updates ALL controls in the dialog box, without concern for their
  1414. ; individual or cumulative values.
  1415.  
  1416.          case 170
  1417.             if strcmpi name "(None)"
  1418.  
  1419. ; if the user selects (None) in the combobox, disable the same controls
  1420. ; as previously, read the default data from the INI file, and then
  1421. ; update the editbox (128) and radiobutton (2) controls in the dialog
  1422.  
  1423.                disable ctrl 230 233
  1424.                disable ctrl 210 211
  1425.                disable ctrl 50
  1426.                disable ctrl 10 11
  1427.                read_ini()
  1428.                updatedlg 130
  1429.             else
  1430.  
  1431. ; otherwise, enable all of those controls previously disabled, read the
  1432. ; INI file data for the chosen system (if any data exists), and then
  1433. ; check to make sure the INI defined mail directory (if there is one) is
  1434. ; valid
  1435.  
  1436.                enable ctrl 230 233
  1437.                enable ctrl 210 211
  1438.                enable ctrl 50
  1439.                enable ctrl 10 11
  1440.                read_ini()
  1441.                updatedlg 130
  1442.                if not null_str(maildir)
  1443.                   source = 1
  1444.                   chk_dir()
  1445.                endif
  1446.             endif
  1447.          endcase
  1448.  
  1449. ; cases 210 and 211 handle a press of either iconbutton in the dialog
  1450. ; box. What is unique here is the use of the SDLGINPUT command. In Wasp,
  1451. ; user defined dialog boxes are not the only ones available from a
  1452. ; script. Wasp may also access "standard" dialogs. These are such things
  1453. ; as standard file open and file save dialogs, and the one used here, a
  1454. ; standard input dialog. There are two advantages to using this form or
  1455. ; dialog... 1) they are simple to use, since they do not have to be
  1456. ; "designed" by the scripter, and 2) they CAN and WILL appear over top
  1457. ; of a user defined dialog box. In Wasp, there is only one user defined
  1458. ; dialog box allowed on screen at any given time. By using standard
  1459. ; dialogs, it is possible to get additional user input without
  1460. ; destroying the original dialog box, and then recreating it when needed
  1461. ; again later.
  1462. ; Both procedures work the same way... bring up a standard input dialog,
  1463. ; asking for a "new" value for the appropriate string. Check if the
  1464. ; string exists, and if so, was it changed. If changed, save the new
  1465. ; information.
  1466.  
  1467.          case 210
  1468.             user2=user
  1469.             redo:
  1470.             sdlginput "UserID" "Enter / Edit the UserID : " user DEFAULT
  1471.             if success
  1472.                if null_str(user)
  1473.                   goto redo
  1474.                endif
  1475.                if not strcmpi user user2
  1476.                   profilewr ini name "userID" user
  1477.                endif
  1478.             endif
  1479.          endcase
  1480.          case 211
  1481.             pword2=pword
  1482.             again:
  1483.             sdlginput "Password" "Enter / Edit the Password : " pword 
  1484. DEFAULT
  1485.             if success
  1486.                if null_str(pword)
  1487.                   goto again
  1488.                endif
  1489.                if not strcmpi pword pword2
  1490.                   profilewr ini name "pword" pword
  1491.                endif
  1492.          endif
  1493.          endcase
  1494.  
  1495. ; case 10 takes care of saving the data set in the dialog box. We first
  1496. ; make sure that all the data we need has been entered, then check to
  1497. ; make sure that the mail directory entered is valid, save the data for
  1498. ; the system, and even check the normal download directory for old mail
  1499. ; packets (QW?) for this system, moving them to the mail directory if we
  1500. ; find any.
  1501.  
  1502.         case 10
  1503.         if not (null_str(user) || null_str(pword) || null_str(board) ||\
  1504.  null_str(door) || null_str(maildir))
  1505.            strlen maildir lendx
  1506.            lendx--
  1507.            strpeek maildir lendx charval
  1508.            if charval == 92
  1509.               strdelete maildir lendx 1
  1510.            endif
  1511.            ren_def = $NULLSTR
  1512.            if rb1 == 2
  1513.               ren_def = "date"
  1514.            endif
  1515.            fetch dnldpath dldir
  1516.            if null_str(ren_def)
  1517.               same_dir()
  1518.               if flag
  1519.                  flag = 0
  1520.                  exitswitch
  1521.               endif
  1522.             endif
  1523.             source=2
  1524.             chk_dir()
  1525.             if flag
  1526.                flag = 0
  1527.                exitswitch
  1528.             endif
  1529.             strfmt save_set "Save configuration for %s?" name
  1530.             sdlgmsgbox "Confirming..." save_set QUESTION YESNO button 1
  1531.             if button == 6
  1532.                strupr maildir
  1533.                strupr board
  1534.                strupr defconf
  1535.                write_ini()
  1536.                is_old_mail()
  1537.                statmsg "Configuration for %s saved!" name
  1538.                updatedlg 130
  1539.             elseif button == 7
  1540.                read_ini()
  1541.                updatedlg 130
  1542.             endif
  1543.          else
  1544.             wrong=$NULLSTR
  1545.             if null_str(user)
  1546.                wrong=str_make(wrong, "UserID")
  1547.             endif
  1548.             if null_str(pword)
  1549.                wrong=str_make(wrong, "Password")
  1550.             endif
  1551.             if null_str(board)
  1552.                wrong=str_make(wrong, "Qmail packet name")
  1553.             endif
  1554.             if null_str(door)
  1555.                wrong=str_make(wrong, "Open door")
  1556.             endif
  1557.             if null_str(maildir)
  1558.                wrong=str_make(wrong, "Mail directory")
  1559.             endif
  1560.             errormsg "Blank Field(s) are not allowed for - %s ." wrong
  1561.          endif
  1562.          endcase
  1563.  
  1564. ; case 11 performs a simple "delete" of a system from PCBMail, by 
  1565. removing
  1566. ; all INI file settings. The system is not really deleted, but all 
  1567. entries
  1568. ; in the INI are set to null. There is no way, when using an INI file
  1569. ; format to actually delete entries once made, other than via a text
  1570. ; editor, so rather than bother with it, we simply set all entries for a
  1571. ; given system to nothing (null) in order to delete it.
  1572.  
  1573.          case 11
  1574.             if not null_str(name)
  1575.                strfmt del_set "Delete configuration for %s?" name
  1576.                sdlgmsgbox "Confirming..." del_set STOP YESNO button 1
  1577.                if button == 6
  1578.                   if not null_str(board)
  1579.                      board = $NULLSTR
  1580.                      door = $NULLSTR
  1581.                      maildir = $NULLSTR
  1582.                      defconf = $NULLSTR
  1583.                      ren_def = $NULLSTR
  1584.                      write_ini()
  1585.                      statmsg "Configuration for %s deleted!" name
  1586.                      updatedlg 130
  1587.                   else
  1588.                      errormsg "%s is not configured!" name
  1589.                   endif
  1590.                endif
  1591.             endif
  1592.           endcase
  1593.       endswitch
  1594.       dlgstatus = $DIALOG
  1595.    endwhile
  1596. endproc              ; end of main procedure
  1597.  
  1598. ; a simple procedure which reads the INI file for data which is stored
  1599. there. Used several times, whenever data in MAIN needs updating.
  1600.  
  1601. proc read_ini
  1602.    profilerd ini name "board_ID" board
  1603.    profilerd ini name "userID" user
  1604.    profilerd ini name "pword" pword
  1605.    profilerd ini name "door_ID" door
  1606.    profilerd ini name "mail_dir" maildir
  1607.    profilerd ini name "def_conf" defconf
  1608.    profilerd ini name "rename_as" ren_def
  1609.  
  1610. ; the dialog box for this script does not use a string variable for the
  1611. ; method of renaming a packet, but rather a radiobutton setting. This
  1612. ; little bit of code sets a value for that radiobutton based on whether
  1613. ; the variable ren_def is null or not.
  1614.  
  1615.    rb1 = 1
  1616.    if not null_str(ren_def)
  1617.       rb1 = 2
  1618.    endif
  1619. endproc
  1620.  
  1621. ; another simple little procedure, this time the reverse of above, i.e.
  1622. ; it writes data to the INI file
  1623.  
  1624. proc write_ini
  1625.    profilewr ini name "board_ID" board
  1626.    profilewr ini name "door_ID" door
  1627.    profilewr ini name "mail_dir" maildir
  1628.    profilewr ini name "def_conf" defconf
  1629.    profilewr ini name "rename_as" ren_def
  1630. endproc
  1631.  
  1632. ; This function may seem strange, since it does exactly what nullstr
  1633. ; does, however, it does serve a purpose. Windows Aspect only allows the
  1634. ; testing of a single nullstr command in an "if" conditional line. By
  1635. ; using this function to replace it, we can test multiple strings for
  1636. ; nulls, making the if...else conditionals simpler and faster.
  1637.  
  1638. func null_str:integer
  1639.    strparm test_str
  1640.    integer result
  1641.    nullstr test_str result
  1642.    return result
  1643. endfunc
  1644.  
  1645. ; all this function does is take two strings passed to it, and
  1646. ; concatenate them. The trick is that it adds a semi-colon and space to
  1647. ; the resulting string, making the string usable as a variable for our
  1648. ; combobox in the main dialog
  1649.  
  1650. func str_make:string
  1651.    strparm parm1, parm2
  1652.    if not null_str(parm1)
  1653.       strcat parm1 "; "
  1654.    endif
  1655.    strcat parm1 parm2
  1656.    return parm1
  1657. endfunc
  1658.  
  1659. ; Fairly straightforward.... if standard renaming is used, the mail
  1660. ; directory and default download directory can not be the same. This
  1661. ; proc just checks for that.
  1662.  
  1663. proc same_dir
  1664.    if strcmpi dldir maildir
  1665.       errormsg "If standard renaming is used, the mail and download 
  1666. directories
  1667.       profilerd ini name "mail_dir" maildir
  1668.       updatedlg 130
  1669.       flag = 1
  1670.    endif
  1671. endproc
  1672.  
  1673. ; Again, fairly straightforward... check to see if the mail directory
  1674. ; set in the dialog exists. If not, ask if the user wants it created.
  1675.  
  1676. proc chk_dir
  1677.    integer button
  1678.    string curr_dir, dirmsg
  1679.    getdir 0 curr_dir
  1680.    if not chdir maildir
  1681.       if source == 1
  1682.          mkdir maildir
  1683.       elseif source == 2
  1684.          strfmt dirmsg "The directory %s does not exist. Create it?" 
  1685. maildir
  1686.          sdlgmsgbox "Warning!" dirmsg EXCLAMATION YESNO button 1 BEEP
  1687.          if button == 6
  1688.             mkdir maildir
  1689.          elseif button == 7
  1690.             flag = 1
  1691.             profilerd ini name "mail_dir" maildir
  1692.             updatedlg 130
  1693.          endif
  1694.       endif
  1695.    endif
  1696.    chdir curr_dir
  1697. endproc
  1698.  
  1699. ; This proc is real fun stuff! <G> After we have saved the information
  1700. ; for a system, check the download directory for old mail packets. If
  1701. ; one (or more) is found, ask the user what to do with them... either
  1702. ; delete them or rename them according to the settings in the dialog
  1703. ; box. A whole bunch of steps just to do something apparently that
  1704. ; simple, huh?
  1705.  
  1706. proc is_old_mail
  1707.    integer button, count, test = 0, char, char2, max = 0, ltr, len, rltr
  1708.    string oldqwk, msg, newqwk, renqwk, oldfile, root, sdate
  1709.    strfmt oldqwk "%s\%s.QW?" dldir board
  1710.    if findfirst oldqwk
  1711.       strfmt msg "Old mail for %s has been found. Rename it? [NO] will 
  1712. delete t
  1713.       sdlgmsgbox "Warning!" msg STOP YESNO button 1 BEEP
  1714.       if button == 6
  1715.  
  1716. ; if we are renaming the mail that was found, we have to determine how
  1717. ; we are going to rename it. If we are using the default scheme (packet
  1718. ; name with a changed extension), things are simple. If we are using the
  1719. ; alternate scheme (up to 4 characters of the packet name, combined
  1720. ; with 4 characters representing the date) then we have to determine
  1721. ; what root we will use for the renamed mail packets
  1722.  
  1723.          if null_str(ren_def)
  1724.             root = board
  1725.          else
  1726.             substr sdate $DATE 0 5
  1727.             strdelete sdate 2 1
  1728.             strfmt root "%s%s" board sdate
  1729.             strlen root len
  1730.             if len > 8
  1731.                len -= 8
  1732.                strdelete root 4 len
  1733.             endif
  1734.          endif
  1735.  
  1736. ; silly though it may seem, we also check the mail directory to make
  1737. ; sure that, if by some chance there already is mail stored there, we
  1738. ; don't overwrite it when renaming and moving the "new" mail.
  1739.  
  1740.          strfmt newqwk "%s\%s.QW?" maildir root
  1741.          if findfirst newqwk
  1742.             max++
  1743.             while 1
  1744.                if findnext
  1745.                   max++
  1746.                else
  1747.                   exitwhile
  1748.                endif
  1749.             endwhile
  1750.          endif
  1751.          max--
  1752.  
  1753. ; now we check to make sure that any packets found in the mail directory
  1754. ; are named properly, and in order. If we find a gap in the extension
  1755. ; (since both renaming sequences use a QW? type of extension) for these
  1756. ; packets in the mail directory, we rename them until they are in proper
  1757. ; order.
  1758.  
  1759.          for count = 0 upto max
  1760.             char = 65 + count
  1761.             strfmt newqwk "%s\%s.qw%c" maildir root char
  1762.             if findfirst newqwk
  1763.                loopfor
  1764.             else
  1765.                for test upto 25
  1766.                   char2 = char + test
  1767.  
  1768.                   strfmt newqwk "%s\%s.qw%c" maildir root char2
  1769.                   if findfirst newqwk
  1770.                      strfmt renqwk "%s\%s.qw%c" maildir root char
  1771.                      rename newqwk renqwk
  1772.                      exitfor
  1773.                   endif
  1774.                endfor
  1775.             endif
  1776.          endfor
  1777.  
  1778. ; and now, we rename and move the mail from the download directory to
  1779. ; the mail directory.
  1780.  
  1781.          ltr = 65
  1782.          rltr = max + 65
  1783.          for count = 0 upto 35
  1784.             strfmt oldqwk "%s\%s.QW%c" dldir board ltr
  1785.             if isfile oldqwk
  1786.                rltr++
  1787.                strfmt renqwk "%s\%s.QW%c" maildir root rltr
  1788.                rename oldqwk renqwk
  1789.                if success
  1790.                   statmsg "%s moved as %s.QW%c" oldfile root rltr
  1791.                else
  1792.                   statmsg "%s not moved!" oldqwk
  1793.                endif
  1794.             endif
  1795.             if count == 25
  1796.                ltr = 47
  1797.             endif
  1798.             ltr++
  1799.          endfor
  1800.       elseif button == 7
  1801.  
  1802. ; If we aren't going to keep the old mail which has been found, this
  1803. ; procedure will delete it.
  1804.  
  1805.          ltr = 65
  1806.          for count = 0 upto 35
  1807.             strfmt oldqwk "%s\%s.QW%c" dldir board ltr
  1808.             if isfile oldqwk
  1809.                delfile oldqwk
  1810.             endif
  1811.             if count == 25
  1812.                ltr = 47
  1813.             endif
  1814.             ltr++
  1815.          endfor
  1816.       endif
  1817.    endif
  1818. endproc
  1819.  
  1820. ***********************************************************************
  1821.  
  1822. Before we continue with an examination of the PCBMAIL.WAS script which
  1823. uses the information obtained through SETMAIL.WAS, I think it's
  1824. appropriate to take a small break from actual scripting (before we
  1825. overload on code <G>). So next time, we're going to discuss some tricks
  1826. and tips for writing a script, including some tips on organizing and
  1827. logical structure, and why this kind of planning can assist you when
  1828. writing a script. Then, we'll look at some tricks and tips which involve
  1829. string manipulation under Wasp, and the use of time variables (BTW, did
  1830. I mention that the Wasp SUSPEND UNTIL command does not work properly,
  1831. and we need to develop work-arounds for that fact?? <GG>)
  1832.  
  1833. David did a rather nice job of discussing the logic and organizational
  1834. skills necessary for script writing in his course on DOS Aspect, but not
  1835. all of you have seen that course (I am posting this in four additional
  1836. nets, along with those two where David originally posted his DOS
  1837. course).
  1838.  
  1839. We won't discuss this too heavily, but I thought that it might be nice
  1840. to take a break from the heavy-duty coding that we have been doing, and
  1841. just relax the tone of things a bit.
  1842.  
  1843. Basically, I suppose that every programmer has his own idea of what
  1844. makes up good programming practice. In my opinion, this good practice
  1845. consists of two basic routines.... planning in advance what you want the
  1846. script to accomplish, and roughly how it might do so, and writing the
  1847. code you need in a modular fashion.
  1848.  
  1849. Let me explain.....
  1850.  
  1851. It is quite difficult to decide what a script (or any code) needs do
  1852. next, when you, the author, have no idea what you want it to do next.
  1853. The code won't "get anywhere" if you, the programmer, have no idea where
  1854. it is that it is expected to go.
  1855.  
  1856. Generally, before I begin work on any code, I attempt to write out, in
  1857. English, what I want the code to  accomplish, and where I want it to end
  1858. up when it is done. This gives me a basic word picture of what I hope
  1859. the code will do when it is finished. To relate this directly to Wasp,
  1860. it also helps to force you to look at what is happening on the terminal
  1861. with a more careful eye, as you attempt to follow what is happening in
  1862. order to create that word picture.
  1863.  
  1864. But... the first rule is to start out simply. Don't get too fancy, and
  1865. don't try to do too much with the first draft of a script. I prefer a
  1866. modular approach to script writing, where one can add features and
  1867. functions simply by adding a new procedure to a basic script. In this
  1868. way, a first draft script can be kept quite simple, and then have other
  1869. routines added to it as they are tested.
  1870.  
  1871. However, this does not preclude increasing the size of the main
  1872. procedure. Often, once a procedure has been tested and found to work
  1873. properly, if it is to be called only that one time, I will simplify the
  1874. structure of the script by removing it from a separate procedure, and
  1875. adding it to the main procedure where the sub-procedure was called from
  1876. initially.
  1877.  
  1878. Basically, when I begin work on a new script, or upon a new procedure to
  1879. be added to a script, I make sure of two things... 1) that I have lots
  1880. of paper for writing the English, flow chart, and code information, in
  1881. rough, and 2) that I have plenty of disk space available for various
  1882. versions of the code being tested., not to mention backups of it, just
  1883. in case! <GG>
  1884.  
  1885. I remember one case where I was away for the weekend, but brought a
  1886. printout of the code I was working on, and a pad of paper to write
  1887. modified code. My daughters complained bitterly that I must have
  1888. destroyed three trees to write that relatively small bit of code,
  1889. because I went through so much paper writing it. But this brings up
  1890. another routine that I use frequently... rather than physically testing
  1891. the code while on line, I often use diagrams, etc. to work out, on
  1892. paper, what should happen, based on the code written, in order to test
  1893. the logic of the script before actually compiling it, and running the
  1894. code.
  1895.  
  1896. And that can be supremely important... the logic of the script,
  1897. especially when trying to track down a "bug" or mistaken action. One of
  1898. the main benefits of using English "coding", and flow charting a script
  1899. is that it tends to improve the logic of the script. Rather than
  1900. bouncing here and there, from one procedure to another, all over the
  1901. code, it becomes easier to write the various procedures and routines in
  1902. a more logical fashion. This makes the script easier to follow later,
  1903. when a problem develops, or you want to change some section of it. A
  1904. logical arrangement of sub-procedures and routines within procedures
  1905. makes it far easier to locate a section which you want to change.
  1906.  
  1907. To that end, I also attempt to use descriptive variable names, and
  1908. procedure/function names, where ever possible. As example, in my PCB
  1909. Freedom script, the procedure which does the physical dialing of a
  1910. system, and manages the on line functions is called "proc dial_boards".
  1911. The procedure which creates a dialog box to modify system settings while
  1912. off line is called "proc edit_dlgs". The routine to add a new system to
  1913. the configuration list is called "proc add_item".
  1914.  
  1915. I am sure that you can see how this might prove advantageous. On a
  1916. little script like our PCBLOG.WAS created earlier, this is not of such
  1917. importance, as the script is fairly short. But PCB Freedom is currently
  1918. around 3,500 lines of Wasp code, and locating a particular section
  1919. within that 3,500 lines can be quite difficult without some "sign
  1920. posts". I use descriptive variable and procedure names, with a logical
  1921. connection to what they are for, as my "sign posts".
  1922.  
  1923. I also use the search and replace feature of my editor to my advantage.
  1924. Often times, a variable, or procedure may start life with a particular
  1925. name, descriptive of it's purpose and place within the logic of the
  1926. script. However, as the script takes on new features and functions, that
  1927. descriptive name may no longer be valid, or useful. When this happens,
  1928. "search and replace" allows for easy change of one name or variable in
  1929. use, into another, which may be more informative under the current code
  1930. in use.
  1931.  
  1932. There is a further feature of my editor which I use regularly, and one
  1933. that I wonder how anyone who writes scripts can do without... that is
  1934. the MDI interface of the Norton DeskTop Editor. Almost without fail,
  1935. when working on a script, I will have more than one file open at a time.
  1936. Sometimes it is both the WAS file and the LIB (which extension I use to
  1937. indicate that it is an "INCLUDE" file for some WAS file) for it, other
  1938. times it is my actual script and a file containing the code segment that
  1939. I have been working on. With a good MDI interface, cutting and pasting
  1940. from one file to another becomes easy. When I am not using Windows to
  1941. work on my code, I use QEdit, which also allows multiple files open at
  1942. the same time (right now, this file and FREEDOM.WAS are both open in
  1943. QEdit, to make referencing procedures, etc. in Freedom easier for me
  1944. <GG>)
  1945.  
  1946. The thing to remember is that there are no "set" rules for methods or
  1947. procedures to write a good script. What works best for one code maven
  1948. may be deadly to another. I know my old CompSci prof from many years
  1949. back would probably have a fit about that comment, as he constantly
  1950. emphasized following rules, etc. when coding, but, in the real world, I
  1951. have found that those "rules" do not always work.
  1952.  
  1953. That prof used to always tell us to never use a GOTO label, but to
  1954. always call a sub-routine instead. Theoretically, this may work, but in
  1955. the real world, there are many occasions when you do not want the
  1956. script to return to a given spot, but want it instead to branch off
  1957. through a different set of code. GOTO works rather well for this, while
  1958. calling a sub-routine can be tricky to do the same sort of thing. And
  1959. since Wasp is not Fortran nor Cobol, and I don't run Procomm on a
  1960. mainframe, I don't think that the rules he used to drill into us are
  1961. applicable.
  1962.  
  1963. And there is one other thing that I find important about breaking the
  1964. rules... you just might discover something that helps! While working on
  1965. the early versions of GHOST BBS, Toby and I would swap beta code around
  1966. (Toby lives around 100 km. from me... we did everything by modem, most
  1967. often using early drafts of GHOST as the means to transfer modified
  1968. code.), and more times than I care to remember, one or the other of us,
  1969. when discussing changes by voice, would say "But you can't do that!"
  1970.  
  1971. The trick is that, sometimes, what conventional wisdom (the rules) says
  1972. can't be done, just might be possible. But you will never discover these
  1973. "huh?" code segments if you follow all of the "rules". In both Freedom
  1974. and GHOST, there are several bits of code which, when I first wrote
  1975. them, were discarded because examination on paper "proved" that I
  1976. couldn't do that. But, when all else failed, or the conventional methods
  1977. grew too cumbersome, I would invariably fall back on the "impossible"
  1978. code, and generally, found that what appeared impossible on paper,
  1979. worked quite well when compiled. <GG>
  1980.  
  1981. However, remember that there is a corollary to this "trick"... sometimes
  1982. what appears, at first test, to work, really is "impossible", and can
  1983. bite you when you least expect it. One pitfall to being a "ground
  1984. breaker" is that sometimes, you find that, instead of "breaking new
  1985. ground", you are over the edge of the cliff with an anvil for a
  1986. parachute.
  1987.  
  1988. Never discount the "impossible"... fairly early in the beta of Freedom,
  1989. I ran into a problem with some very strange responses being sent by the
  1990. script, when the code was written to delete all characters from the
  1991. string variable, resulting in a null string, and nothing being sent.
  1992. When I discussed it with the folks from Datastorm, I was told that what
  1993. I claimed to have happening was impossible, and that deleting one by
  1994. one, the characters of a string HAD to result in a null string when the
  1995. last character was deleted.
  1996.  
  1997. Further testing on my part, and very careful observation (and notes) of
  1998. what was being sent when nothing should have been sent, showed me that,
  1999. somehow, the string, once all characters were deleted, was being
  2000. assigned a value which seemed to be the central six characters from the
  2001. LAST string variable accessed before the current string variable had
  2002. it's last character deleted (does this make sense?? <G>) The solution...
  2003. when the last character was supposed to be deleted from the string,
  2004. instead of actually deleting it, I began assigning the system variable
  2005. $NULLSTR to the string variable. Now, impossible or not, the string
  2006. variable actually was a null when I wanted and expected it to be one.
  2007.  
  2008. Datastorm still says the results that I experienced are impossible, but
  2009. I can duplicate them any time I wish by restoring the original code
  2010. segment to that function. I must agree with Datastorm in that,
  2011. logically, the results are absolutely impossible, however, it did
  2012. happen, on my system, and several beta sites.
  2013.  
  2014. In other words, never say never, and always suspect that what is not
  2015. possible for a script to do, just may be possible. (and never turn your
  2016. back on a "completed" and "fully tested" script.... they can be mean,
  2017. vicious and downright despicable! <GG>)
  2018.  
  2019. I suppose that I have rambled enough for now. I just thought that a
  2020. break from "code" might be nice here, and that telling you of some of my
  2021. experiences with planning and writing scripts might help you establish
  2022. some procedures for writing scripts which work for you....
  2023.  
  2024. System Variables
  2025. ----------------
  2026.  
  2027. We have already seen and used quite a few "system" variables, such as
  2028. $PASSWORD, $D_NAME, etc. The uses for these pre-defined and reserved
  2029. system variables in a script can be numerous. Some of them contain
  2030. basic operations data for PCP/Win which can be invaluable in a script
  2031. (such as $PWTASKPATH, the directory where PW.EXE, the main ProComm
  2032. executable, is stored, and $ACTIONBAR, which returns the current status
  2033. of the icon bar on screen), others contain information on hardware
  2034. states (such as $DIALING and $FLOWSTATE), information on files ($FATTR
  2035. and $FDATE), and still others hold data on different actions taken by
  2036. scripts, allowing us to check their current status ($FILEXFER, $OBJECT
  2037. and $DIALOG)
  2038.  
  2039. Be careful when using some of these, however... some of them do not hold
  2040. a value for long and this can confuse a script. As example....
  2041.  
  2042. while 1
  2043.    if $DIALOG == 50 || $DIALOG == 30
  2044.       do_something()
  2045.       destroydlg
  2046.       exitwhile
  2047.    endif
  2048. endwhile
  2049.  
  2050. Logically, this should check to see if the user has accessed either a
  2051. radiobutton or update pushbutton in a dialog, and if they have, call a
  2052. subroutine, destroy the dialog box when we return from that subroutine,
  2053. and then exit the "perpetual loop" setup of the while/endwhile
  2054. construct.
  2055.  
  2056. However, pressing the update pushbutton, in this case, would be rather
  2057. disappointing... nothing would ever happen. Why? $DIALOG is a system
  2058. variable which is CLEARED TO 0 AFTER IT IS ACCESSED! Thus, in the above
  2059. code, we "access" $DIALOG, and evaluate the expression "30 == 50" (since
  2060. we pressed an update pushbutton, the value of $DIALOG would be 30),
  2061. which is false. So the IF then evaluates the second expression on the
  2062. line, and also determines that it is false.
  2063.  
  2064. "Huh??", you say... "I selected the update button, so the value of 
  2065. $DIALOG
  2066. is 30, and the expression should be true. You just said so above!"
  2067. Well, "0 == 30" is false, is it not? And the value of $DIALOG is now 0,
  2068. not 30, since our test for the first expression has already accessed
  2069. $DIALOG, and Wasp has cleared it before we evaluate the second
  2070. expression.
  2071.  
  2072. Watch out for this with the following variables...
  2073.  
  2074. $DIALOG - cleared to 0 after it is accessed
  2075.  
  2076. $FILEXFER - if the value of the variable is either 2 or 3 (a transfer
  2077. completed either successfully, or unsuccessfully), it is reset to 0
  2078. after being accessed.
  2079.  
  2080. $MENU - cleared to 0 after it is accessed
  2081.  
  2082. $OBJECT - cleared to 0 after it is accessed
  2083.  
  2084. $PKRECV - cleared to 0 after it is accessed
  2085.  
  2086. $PKSEND - cleared to 0 after it is accessed
  2087.  
  2088. The easiest way to avoid this problem is to access the system variable
  2089. only once, when assigning it's value to a user defined (global or local,
  2090. although local works easiest) variable. Then, rather than checking the
  2091. system variable, check the value stored in the user defined variable. As
  2092. example, using the previous code....
  2093.  
  2094. while 1
  2095.    dlgstat = $DIALOG
  2096.    if dlgstat == 50 || dlgstat == 30
  2097.       do_something()
  2098.       destroydlg
  2099.       exitwhile
  2100.    endif
  2101. endwhile
  2102.  
  2103. This one would work as you would expect.
  2104.  
  2105. But there is one additional "type" of system variable which I have
  2106. deliberately not mentioned yet, nor used in the scripts that we have
  2107. been working with. These consist of pre-defined global variables for
  2108. each type of variable possible in a Wasp script, i.e. S0, I0, F0, and
  2109. L0. There are 10 of each type of variable allowed, i.e. S0 through S9,
  2110. etc., but one should use these with caution.
  2111.  
  2112. These variables need not be defined in our script, and furthermore, are
  2113. the only variables which can be "passed" from one script to another by
  2114. using the EXECUTE command. They have one additional advantage, being the
  2115. fact that they already exist in memory, and if you are running short of
  2116. memory during a compile of a script, replacing declared global variables
  2117. with one or more of these pre-defined system variables can often regain
  2118. enough free memory to add a few more lines of code (we'll discuss this
  2119. further in a later "lesson")
  2120.  
  2121. There are, however, many drawbacks to using these variables....
  2122.  
  2123. 1) Since they can be passed to a script called via the EXECUTE command,
  2124. and the value they contain when the child script ends is passed back to
  2125. the parent script, they are too easily inadvertently modified in such a
  2126. way as to lose control of script actions based on their value. If you
  2127. use a user defined global, it's value can not be "passed" to a child
  2128. script, however, likewise, it's value can not be modified inadvertently
  2129. by the child script.
  2130.  
  2131. 2) Their names are not exactly descriptive, in keeping with our logic of
  2132. variable names. Thus, six weeks from now, that script which was clear as
  2133. a bell today, may be of absolutely no use to you, since you can't
  2134. remember what the value of I3 is used for later in the script.
  2135.  
  2136. 3) They suffer from the basic drawback of all global variables, i.e.
  2137. they can be too easily modified in an obscure procedure, resulting in
  2138. totally unexpected values elsewhere in the script, and totally
  2139. unexpected, and often, inexplicable, actions based on those values.
  2140.  
  2141. In the end, there are times when using these predefined globals can be
  2142. quite useful, and the only way to go. When GHOST BBS 2.00 is released,
  2143. it will have the capability to shut itself down at a preset time, and
  2144. run a secondary script. When it does so, it also will pass, to that
  2145. secondary script, either an integer variable, or a string variable, or
  2146. both. Of course, the secondary script will need be written to act on
  2147. those passed variables (PCB Freedom 1.50 will act on the integer, using
  2148. a 1 to tell it to dial the listed systems right away, and when done,
  2149. shut down to return control to GHOST BBS. A 2 passed to Freedom will do
  2150. the same thing, but will eliminate the "System Options" dialog box), but
  2151. the method of passing the variables is, of course, through these
  2152. predefined global system variables, and there is no other way of doing
  2153. so.
  2154.  
  2155. Both GHOST BBS and PCB Freedom use other system variables for other
  2156. reasons (mostly to save memory <G>). One method which I have used
  2157. successfully to remind myself of which system variables are in use, and
  2158. what they are being used for, is to add a comment section at the
  2159. beginning of the script to remind me of this information. Something
  2160. which you may want to do, if you use a system variable such as these in
  2161. your scripts.
  2162.  
  2163.  
  2164. #DEFINE Macros
  2165. --------------
  2166.  
  2167. Defined macros have a multitude of uses in a Wasp script. In their
  2168. simplest form, they represent a variable which may have a different
  2169. value for different users of a script, but need be set only once. As
  2170. example, suppose that your script assumes that a user will have the same
  2171. "name" on all systems which he calls. If you were to use a line like
  2172. this :
  2173.  
  2174. #DEFINE USERID "Gregg Hommel^M"  ; replace with your name and compile
  2175.  
  2176. at the beginning of your WAS file, the user could replace the quoted
  2177. text with his name, and then compile the script. In the WAS, any
  2178. appearance of USERID would be replaced by the string which is defined in
  2179. the line above.
  2180.  
  2181. This, of course, is perhaps one of the simplest uses of a defined macro.
  2182. Once common one is to make reading your source code more understandable.
  2183. As example, suppose that, throughout your script, you tested quite a few
  2184. variables for a true or false state (false being a value of 0, true
  2185. being a value of 1, for an integer variable). Testing for this is quite
  2186. simple, and can be written as follows :
  2187.  
  2188. if my_var == 1               ; if my_var is set to true
  2189.  
  2190. or even
  2191.  
  2192. if my_var
  2193.  
  2194. however, six months from now, would it not be simpler to look at this
  2195. line?
  2196.  
  2197. if my_var == TRUE
  2198.  
  2199. In my opinion, this is much easier to read, and understand. To
  2200. accomplish a test such as this requires two statements in your script
  2201. prior to the main procedure (i.e. in the section for declaration of
  2202. global variables) :
  2203.  
  2204. #DEFINE FALSE 0
  2205. #DEFINE TRUE  1
  2206.  
  2207. From then on in the script, every occurrence of the macro FALSE would be
  2208. replaced at compile time, with a 0, and every occurrence of TRUE with a
  2209. 1. The tests would be valid, but the WAS file would be far more
  2210. readable.
  2211.  
  2212. We use a similar "trick" in GHOST BBS. Although they are not used all
  2213. that often (actually only about 3 times in two procedures), the
  2214. information stored for mail in GHOST includes data regarding it's
  2215. "public" state, i.e. if it is private, received, etc. These are integer
  2216. values. However, when reading the source code, it is difficult to
  2217. remember that 0 is public, 1 private, etc. etc. So, we used #DEFINE
  2218. macros for those values, and now it is simple. Reading code such as
  2219.  
  2220. flag = PRIVATE + NEWMAIL   or
  2221. flag = PUBLIC + DELETED
  2222.  
  2223. is much easier than remembering what
  2224.  
  2225. flag = 3    or
  2226. flag = 4
  2227.  
  2228. means <GG>.
  2229.  
  2230. A further use for defined macros is to replace frequently used code
  2231. strings. An example of this is from the coding of GHOST BBS. In order to
  2232. improve the speed of screen redraws, one thing that Toby and I did was
  2233. change from scrolling screens to redrawn screens. To do this locally in
  2234. the script is simple.. the Wasp CLEAR command clears a local screen,
  2235. resulting in a redraw of the next screen. However, to accomplish the
  2236. same procedure on a remote user's machine is a touch more complicated.
  2237. What this requires is the transmission to the user's machine of the ANSI
  2238. clear screen command (^L).
  2239.  
  2240. In order to increase the speed of displays in GHOST, this command must
  2241. be transmitted frequently (I think the last time I checked, it appeared
  2242. some 250 times in the code), but it is ALWAYS the same command. To
  2243. simplify the code for GHOST, and so that we would not have to remember
  2244. that ANSI code each time we needed it, we placed this line at the
  2245. beginning of the source code for GHOST BBS...
  2246.  
  2247. #DEFINE CLS     transmit "^L^M"   ; send ANSI clear screen to remote
  2248.  
  2249. Now, in the source code for GHOST, instead of having to remember what
  2250. the ANSI code for a transmitted clear screen is, we merely remember the
  2251. DOS level command for it, CLS, and put that in the code (all 250 times
  2252. <G>)
  2253.  
  2254. There is another use for #DEFINE macros which I like (and I am sure that
  2255. you will think of many other ways to use them <g>), although I have yet
  2256. to use it a great deal. This is conditional compilation....
  2257.  
  2258. Although this is not a prime example of a use for this, it will serve to
  2259. point out how it can be used. Let's imagine that you have written a
  2260. script to log onto many different BBS. In one way, it is similar to my
  2261. PCB Freedom script.... the default WAX file includes support for
  2262. encryption of things like passwords in the INI file. The procedures for
  2263. that encryption are proc encrypt and proc uncrypt, which are stored in a
  2264. library, SECURITY.LIB, which, for obvious reasons you can't give out to
  2265. users. Equally as obviously, without that LIB file, they can't compile
  2266. the source code for the rest of the routines themselves.
  2267.  
  2268. However, many of the people using your script say that they don't need
  2269. or want that encryption storage, and are quite happy if their passwords
  2270. are stored in clear text. Do you write a separate script for them to use
  2271. if that is the case?
  2272.  
  2273. I suppose you could, releasing two versions of the file, one with and
  2274. the other without encryption. But there is another way to do it....
  2275.  
  2276. Suppose at the beginning of your source code (the portion which you can
  2277. release to the public), you put something like :
  2278.  
  2279. #DEFINE USESEC "yes"
  2280.  
  2281. In this case, the actual contents of the macro don't really matter. What
  2282. does matter is the definition itself!
  2283.  
  2284. Later in your script, the following code exists.....
  2285.  
  2286. #IFDEF USESEC
  2287.    #INCLUDE "SECURITY.LIB"
  2288. #ENDIF
  2289.  
  2290. and further on, when you read the password from the INI, this code is
  2291. there :
  2292.  
  2293. #IFDEF USESEC
  2294.    pword = uncrypt(pword)
  2295. #ENDIF
  2296.  
  2297. What happens here?? Well, basically, you are telling the script to
  2298. function differently at compile time, and run time, IF a certain macro
  2299. is defined (what it is defined as is immaterial.. only that it is or is
  2300. not defined). In other words, at compile time, IF the macro USESEC is
  2301. defined, include the SECURITY.LIB file during compilation. And, when
  2302. running, if the macro USESEC is defined, decrypt the stored password.
  2303.  
  2304. So, how does this "help" your users?? Well, you can issue a WAX file
  2305. based on the macro being defined. It will use your security procedures
  2306. based on those on your system when you compiled it. But, if a user does
  2307. not want to use these "secure" passwords, he can take your "safe" source
  2308. code (i.e. the WAS without the LIB file), comment out the one line
  2309. "#DEFINE USESEC "yes"" and compile the result. It will not look for the
  2310. SECURITY.LIB file, and will not decrypt the password when it reads it.
  2311.  
  2312. I know that this is not a very good example, but I think you get the
  2313. drift... the script compiled "conditionally", i.e. if a macro is
  2314. defined, it compiles one way, if the macro is not defined, it compiles
  2315. another way.
  2316.